Josh Posted April 6, 2018 Share Posted April 6, 2018 I was wondering if there is any sane / simpler way to sync object data between threads other than creating a complicated messaging system like below? This is going to take a very long time to write all the possible values that might be modified: const int THREADMESSAGE_CREATECAMERA = 1; const int THREADMESSAGE_UPDATEENTITYORIENTATION = 2; shared_ptr<Camera> CreateCamera(world) { shared+ptr<Camera> camera = make_shared<Camera>() camera->world = world; //Add instruction to command queue Instruction instruction; instruction.id = THREADMESSAGE_CREATECAMERA; instruction.argument[0] = camera; instructions.push_back(instruction); return camera; } void Entity::SetPosition(const Vec3& position) { this->position = position; UpdateMatrix(); //Add instruction to command queue Instruction instruction; instruction.id = THREADMESSAGE_UPDATEENTITYMATRIX; instruction.argument[0] = this; instruction.argument[1] = this->mat; instructions.push_back(instruction); } It seems like there should be a better way to do this in a more automated fashion, perhaps with templates and function / member pointers: //Add instruction to command queue AddInstruction( this, &Entity::color, this->color ); Some messages aren't just setting a member, some are a more complex function. This function, for example, would require additional code to send the position data to the VBOs on the graphics card: //Add instruction to command queue AddInstruction( this, &Surface::SetVertexData, this->positiondata->Copy() ); So what we want is some kind of Instruction object that can accept an object, a method or member pointer, and a list of arguments of any type. There's got to be something like that out there already(?) Here is code to get the pointer to a member and a method, and to use them: // declare pointer to member Vec3 Entity::*member = &Entity::position; // declare a pointer to method void (Entity::* method) (const float, const float, const float, const bool) = &Entity::SetPosition; auto entity = Pivot::Create(); entity->*member = Vec3(0); (entity->*method) (1,2,3,false); And then a templated class, something like this?: template <class T, class Y, class P> class Instruction { shared_ptr<T> o; <T>::*(something) function; arguments<Y>[...] args; Instruction(shared_ptr<T> o, arg0<Y>, arg<P>...) { this->o = o; for each argument { arguments[n] = arg[n]; } } void Execute() { o->*function(arg[0],arg[1],arg[2]...); } } Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Rick Posted April 6, 2018 Share Posted April 6, 2018 Looks like variadic templates is what you're looking for? https://stackoverflow.com/questions/1579719/variable-number-of-parameters-in-function-in-c You could use macros. You could create Instruction1<T>, Instruction2<T,A>, Instruction3<T,A,S> etc etc With modern compilers using std:function might be the way to store function pointers vs the old way. Quote Link to comment Share on other sites More sharing options...
Josh Posted April 7, 2018 Author Share Posted April 7, 2018 More discussion here: https://github.com/ThePhD/sol2/issues/623 Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Josh Posted April 7, 2018 Author Share Posted April 7, 2018 Oooh I might have the answer here: http://en.cppreference.com/w/cpp/utility/functional/function Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Josh Posted April 8, 2018 Author Share Posted April 8, 2018 I got it working. The syntax is nice and simple: class Foo { int num = 1; public: void Print() { printf("%d\n",num); } void Add(int n) { num += n; } }; std::vector<std::function<void()> > commandbuffer; auto f = make_shared<Foo>(); commandbuffer.push_back(std::bind(&Foo::Add, f, 1)); commandbuffer.push_back(std::bind(&Foo::Print, f)); commandbuffer.push_back(std::bind(&Foo::Add, f, 36)); commandbuffer.push_back(std::bind(&Foo::Print, f)); commandbuffer.push_back(std::bind(&Foo::Print, f)); for (int n = 0; n < commandbuffer.size(); ++n) { commandbuffer[n](); } Output: 2 38 38 Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Josh Posted April 8, 2018 Author Share Posted April 8, 2018 I don't think there is any way to bind a member to be set to a value, without a setter function, but that's okay. This does what I need. Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Crazycarpet Posted April 8, 2018 Share Posted April 8, 2018 On 4/8/2018 at 3:02 AM, Josh said: I don't think there is any way to bind a member to be set to a value, without a setter function, but that's okay. This does what I need. You should just use a lambda expression? Color4 color(255, 0, 0, 255); // I forgot what structure LE's colors are. AddInstruction( [this, color]() { this->color = color; } ); AddInstruction( [this]() { this->positiondata->Copy(); ); // Or whatever youre doing for the function.... It might pay off to just use lambdas and std::function. This is the most common practice for thread "job pools". It is likely to be more or less the exact same, they're both not pretty to read but using a lambda and it's capture list along with std::vector<std::function<void()>> to hold these functions gives you a lot more freedom in the long run. It is not going to limit you any more as in the lambda you can edit public member variables without a setter function, for private members you would still need a setter but that's obvious. For bind you need a setter regardless of if the member is public/private. I generally do a design similar to this: https://github.com/SaschaWillems/Vulkan/blob/master/base/threadpool.hpp Sascha released it under the MIT license, you should use it. It is more or less exactly what you need. Quote Link to comment Share on other sites More sharing options...
Josh Posted April 8, 2018 Author Share Posted April 8, 2018 It looks like lambdas and bindings can both be stored in the same vector. This is very very good. Great stuff. ? Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Josh Posted April 9, 2018 Author Share Posted April 9, 2018 This is great. Awesome stuff. I did not know you can do this with C++, it makes my life a lot easier: class Foo { public: int num=1; void Print() { printf("%d\n",num); } void Add(int n) { num += n; } }; std::vector<std::function<void()> > commandbuffer; auto f = make_shared<Foo>(); commandbuffer.push_back(std::bind([f]() { f->num = 100; })); commandbuffer.push_back(std::bind(&Foo::Add, f, 1)); commandbuffer.push_back(std::bind(&Foo::Print, f)); commandbuffer.push_back(std::bind(&Foo::Add, f, 36)); commandbuffer.push_back(std::bind(&Foo::Print, f)); commandbuffer.push_back(std::bind(&Foo::Print, f)); for (int n = 0; n < commandbuffer.size(); ++n) { commandbuffer[n](); } 1 Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Crazycarpet Posted April 9, 2018 Share Posted April 9, 2018 Nice, I guess it makes sense they both return a function, I did not ever think that they would work together. Good to know. If std::bind is returning a std::function<void()>, it is likely you don't need to wrap the lambda in a call to std::bind() either. Keep in mind std::bind is for class members, not lambdas. 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.