Jump to content
  • Advertisement
Sign in to follow this  
Tispe

C++ Polymorphic ECS | error C2385: ambiguous access of 'set'

Recommended Posts

Hi

I want to test out a polymorphic entity component system where the idea is that the components of an entity are "compositioned" using templated multiple inheritance. But I am running into an issue because I am stacking a bunch of methods with the same names inside a class (but they have different signatures). I want these methods to be overloaded by the template type but my compiler says the access is ambiguous. I have issues making them unambiguous with the using declaration because the paramter pack expansion causes a syntax error.

Can anyone here give me some advice on this?

 

template <class T>
class component
{
	T m_data;
protected:
	component() {};
	~component() {};
public:
	void set(const T& data) { m_data = data; };
};

template <class ...Ts>
class entity : public component<Ts>...
{
public:
	entity() {};
	~entity() {};
	//using component<Ts>::set...;		// syntax error
};

struct position { float x{}; float y{}; float z{}; };
struct velocity { float x{}; float y{}; float z{}; };

int main()
{
	entity<position, velocity> myEntity;

	position pos = { 1.0f, 1.0f, 1.0f };
	velocity vel = { 2.0f, 2.0f, 2.0f };

	myEntity.set(pos);		// error C2385: ambiguous access of 'set'
	//myEntity.set(vel);

	return 0;
}

 

Share this post


Link to post
Share on other sites
Advertisement

Bit of a sidebar, but I'm curious as to why are you trying to do this in the first place? First of all, this isn't actually ECS, because a fundamental aspect of ECS is that composition is done at run-time. What you have here is a complicated way to use multiple inheritance to make compile-time composition happen. The problems you're running into are one of the many reasons we don't generally use multiple inheritance. What does this approach give you over plain old composition (which we normally prefer over inheritance) or plain old multiple inheritance for that matter?

Edited by Oberon_Command

Share this post


Link to post
Share on other sites

For some reason the compiler cannot resolve which base class set() is to be called (someone more experienced might be able to shed light on the exact issues here).

The solution seems to be to do something like this:

template <class ...Ts>
class entity : public component<Ts>...
{
public:
    entity() {};
    ~entity() {};
  
    template <class U>
    void set(const U& data) { component<U>::set(data); }
};

 

Edited by TheComet

Share this post


Link to post
Share on other sites

The issue is that the compiler (By standard) may not resolve function overloads which reside in multiple templated bases, you need to explicitly define which base to search in as such:

template<typename T>
struct Base {
	void Foo(const T& value) {}
};

template<typename... T>
struct Front : public Base<T>... {
	template<typename U>
	void ExplicitFoo(const U& value) {
		Base<U>::Foo(value);
	}
};

int main() {
	Front<int, char> front;
  
	front.Foo(5);         // ERROR: Ambigous (but not really)
	front.ExplicitFoo(5); // OK
  
	return 0;
}

 

Edited by Migi0027

Share this post


Link to post
Share on other sites

Why would you inherit publicly from component<position> and component<velocity>? That code is telling me that your entity is a component<position> and it's also a component<velocity>, whatever those mean. I very much doubt public inheritance is the correct construct to use here.

 

Share this post


Link to post
Share on other sites

The example I gave is just a simplification to get to the issue, ambiguous access from multiple inheritance.

The component here is actually going to hold (or for that matter be) a proxy pointer to the data allocated somewhere else (in the system<T>).

Should the system move the data to another memory location this component will have its proxy pointer updated by the system. The proxy pointer is built from bi-directional smart pointers to ensure that a component get/set is only one indirection away, and should either the component be destructed or the data no longer be available in the system get/set and update proxy pointer will not crash the program.

With this I can create an entity and select its components using templates. I can also give it overloaded operators, such as, myEntity = pos1; and, myEntity = vel2;

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!