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

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  

  • Forum Statistics

    • Total Topics
      628282
    • Total Posts
      2981822
  • Similar Content

    • By noodleBowl
      I was wondering if anyone could explain the depth buffer and the depth stencil state comparison function to me as I'm a little confused
      So I have set up a depth stencil state where the DepthFunc is set to D3D11_COMPARISON_LESS, but what am I actually comparing here? What is actually written to the buffer, the pixel that should show up in the front?
      I have these 2 quad faces, a Red Face and a Blue Face. The Blue Face is further away from the Viewer with a Z index value of -100.0f. Where the Red Face is close to the Viewer with a Z index value of 0.0f.
      When DepthFunc is set to D3D11_COMPARISON_LESS the Red Face shows up in front of the Blue Face like it should based on the Z index values. BUT if I change the DepthFunc to D3D11_COMPARISON_LESS_EQUAL the Blue Face shows in front of the Red Face. Which does not make sense to me, I would think that when the function is set to D3D11_COMPARISON_LESS_EQUAL the Red Face would still show up in front of the Blue Face as the Z index for the Red Face is still closer to the viewer
      Am I thinking of this comparison function all wrong?
      Vertex data just in case
      //Vertex date that make up the 2 faces Vertex verts[] = { //Red face Vertex(Vector4(0.0f, 0.0f, 0.0f), Color(1.0f, 0.0f, 0.0f)), Vertex(Vector4(100.0f, 100.0f, 0.0f), Color(1.0f, 0.0f, 0.0f)), Vertex(Vector4(100.0f, 0.0f, 0.0f), Color(1.0f, 0.0f, 0.0f)), Vertex(Vector4(0.0f, 0.0f, 0.0f), Color(1.0f, 0.0f, 0.0f)), Vertex(Vector4(0.0f, 100.0f, 0.0f), Color(1.0f, 0.0f, 0.0f)), Vertex(Vector4(100.0f, 100.0f, 0.0f), Color(1.0f, 0.0f, 0.0f)), //Blue face Vertex(Vector4(0.0f, 0.0f, -100.0f), Color(0.0f, 0.0f, 1.0f)), Vertex(Vector4(100.0f, 100.0f, -100.0f), Color(0.0f, 0.0f, 1.0f)), Vertex(Vector4(100.0f, 0.0f, -100.0f), Color(0.0f, 0.0f, 1.0f)), Vertex(Vector4(0.0f, 0.0f, -100.0f), Color(0.0f, 0.0f, 1.0f)), Vertex(Vector4(0.0f, 100.0f, -100.0f), Color(0.0f, 0.0f, 1.0f)), Vertex(Vector4(100.0f, 100.0f, -100.0f), Color(0.0f, 0.0f, 1.0f)), };  
    • By Rannion
      Hi,
      I'm trying to fill a win64 Console with ASCII char.
      At the moment I have 2 solutions: one using std::cout for each line, let's say 30 lines at once using std::endl at the end of each one.
      The second solution is using FillConsoleOutputCharacter. This method seems a lot more robust and with less flickering. But I'm guessing, internally it's using a different table than the one used by std::cout. I'm trying to fill the console with the unsigned char 0xB0 which is a sort of grey square when I use std::cout but when using FillConsoleOutputCharacter it is outputted as the UTF8 char '°'.
      I tried using SetConsoleOutputCP before but could not find a proper way to force it to only use the non-extended ASCII code page...
      Has anyone a hint on this one?
      Cheers!
    • By Vortez
      Hi guys, i know this is stupid but i've been trying to convert this block of asm code in c++ for an hour or two and im stuck
      ////////////////////////////////////////////////////////////////////////////////////////////// /////// This routine write the value returned by GetProcAddress() at the address p /////////// ////////////////////////////////////////////////////////////////////////////////////////////// bool SetProcAddress(HINSTANCE dll, void *p, char *name) { UINT *res = (UINT*)ptr; void *f = GetProcAddress(dll, name); if(!f) return false; _asm { push ebx push edx mov ebx, f mov edx, p mov [ebx], edx // <--- put edx at the address pointed by ebx pop edx pop ebx } return res != 0; } ... // ie: SetProcAddress(hDll, &some_function, "function_name"); I tried:
      memcmp(p, f, sizeof(p)); and UINT *i1 = (*UINT)p; UINT *i2 = (*UINT)f; *f = *p; The first one dosent seem to give the right retult, and the second one won't compile.
      Any idea?
    • By adapelin
      I am a computer engineering student and i have the assignment below. İ only can write the 2D maze array and have no idea about creating car and time as well. Could anyone write and explain hot to do???
      Minimum Criteria: You are expected to design the game by using C ++ . Below are the minimal criteria: • You must create game board with 2 - Dimensional Matrix • Bonuses create with randomly in the game board • All bonuses have got the same value but different effect for car and score . These effects may be positive or negative . • You must use pointer for creating and using car . Some bonuses may be change car type. • When the game finish, you must show high - score. • For moving car , you need to create coordinate s randomly and you need to write proper control statements. • You must use functions for drawing game board and changing car type . If you need extra functions, you can use it. • If you cannot get out the maze when the time is up , the game is over and you need to show high score. In this project, you must do all minimum criteria. In the end, your program must be work without any errors. Bonus: • Save and load high score information to/from disk • Each bonus has got different random values. • You can create cheat codes for the game. • You can create alternative control for car . • Car can jump over the wall but may lose the score . When car exit the maze , game is over and you need to show high score.    
    • By Angelic Ice
      Hey everyone!
      Text is down below, just wanted to provide some C++ish pseudo-code to help me expressing my issue ('...' defines omitted details):
      class Interface_Tree { public: ... } class Tree : public Interface_Tree { public: ... private: std::vector<Interface_Bucket> buckets; } class Interface_Bucket { public: ... add_object(Interface_Object* obj) } class Special_Bucket : public Interface_Bucket { public: add_object(Interface_Bucket_Object* obj) { /* If `Interface_Bucket_Object` cannot be cast to `Special_Bucket_Object_Category`, do nothing. This would require a dynamic_cast which I want to avoid, that probably cannot unless restructuring. Check what enum-type `Special_Bucket_Object_Category` If `VERY_IMPORTANT` use pointer `very_important_obj`. Else add to `objs`. */ } private: std::vector<Interface_Bucket_Object*> objs; Interface_Bucket_Object* very_important_obj; } class Interface_Bucket_Object { public: ... } enum Special_Bucket_Object_Category { IMPORTANT, VERY_IMPORTANT, SPAM }; class Special_Bucket_Object : public Interface { public: ... private: Special_Bucket_Object_Category category; } I wanted to start programming to interfaces (or in C++, virtual classes) instead of actual classes (which are now just derived ones) in order to improve unit-testing etc., hence every class derives a base, that can be easily implemented differently.
      But the issue is, if I have a Hash-Bucket data-structure class owning buckets that collect bucket-objects, how can the bucket differentiate characteristics of an implementation of the implemented bucket-object? Sure, I can do a dynamic-cast, if that fails just abort, but I do not want to do a dynamic-cast, because I would rather restructure my code.
      I want my bucket-objects to have a flag (could also be called tag, category, ...) that notifies a bucket that this object needs the entire bucket, hence the enum in my code expressing the flags an object can raise.
      If I would omit all interface-stuff, it would be fairly easy. My Bucket's add_object() method could easily only be called with that exact object and since the exact bucket-object type is now given, and not just some interface, accessing the enum is trivial via a getter-method.
      And a last reminder: I'm not using virtual-classes to profit from inheritance in a sense of a bucket being able to collect a tons of different implementations, just one implementation that carries an enum. The bucket-tree won't own different types of buckets neither, just one implementation kind of bucket. The only reason I use inheritance is to enable easy unit-testing.
      So, how can I access the object's category without dynamic-casting?
       
       
  • Popular Now