yes that took my deepest knowledge of CPP.
in the pass I implemented by using classes that I send to the thread pool, you can check it in 3.14 and the early release on 4.00
the problem with classes, is that it requires many parament setting, but since you can pass them ion a constructor because the only way to initializes an array of classes in cpp is by giving the a copy constructure so the loop function was very ugly, reading the parameter before in enter the loop.
that bother me for a long time.
one nice way to fix that is by making a class with a function operator().
It is difficult to find info about how that operator is use. in fact until c++ 11 the operator() did no really worked in c++ because is was upto the compiler maker how to do it.
so documenting is very sparce.
basically, is a function that let you call a class with an arbitrary set of parameters. here is a test
form c++ 11 the implementation is standardized but it is still hard to use.
here is an example:
https://cplusplus.com/reference/functio ... ator_func/you see that using the operator, you can make an array of objects and pass parameters. so I was ready to use that.
however it is still not quite that easy, because now I have the problem that each class will have a different set of paraments, or I have to pass pointer to the parent object and dereference them.
here is where lambda come to the rescue. a lambda function is in fact a function call operator()
but in addiction that have the extra feature call the closure, implemented by the symbol []
you can think of the closure and global variable that you can pass to the class.
for example say you make a lambda
auto UpdateAabb = [] (int a, int b) {return a + b};
that will add a + b, nothing special
but now let use say we nee to read a value from the parent class, say c which is a member of the caller class, of course you can always add another lambda wit three parameters
auto UpdateAabb = [] (int a, int b, int c) {return a + b + c};
as you can see, it gets very tedious very quickly. Specially if you want call place them in an array and the simply call the function pointer from the array. for example say a in a thread index, and b is an thread count. for example
auto UpdateAabb = [] (int threadIndex, int thread Count) {return c[index] + a};
now if you have a thread pool and you wnat to call each thread with the inde of teh tread, all you do is you make the array of objects, whi all have the default construtors, you pass that array to the thread pool and the thread pool iterate over the array calling
foreach thread (i)
obj[i](threadid, thread);
that can be made into a system, but it remains how to set the parameters,
and that where the closure come in,
you can make your lamdda with a set of closre argumen will all be pass as member variavle of the
somethimg like:
auto UpdateAabb = [this] (int threadIndex, int thread Count) {return c[index] + a};
now you see that the lambda function does not have to pass this as argument, instead the object is created add a member variable as set it to this.
you can do that by hand but by it is also very hard because each class will have to have teh same type. it is better to make a template function that will do the proper initialization. o fthe array of objects, I did it in file
C:\Development\newton-dynamics\newton-4.00\sdk\dCore\ndThreadPool.h
the function is
- Code: Select all
namespace ndMakeObject
{
template<typename Type> auto ndFunction(const Type & obj) -> decltype (::ndFunction<Type>(obj))
{
return ::ndFunction<Type>(obj);
}
}
so what that does is that take the lambda function, and make one object on the stack and place the closure argument as member variables.
them I call the function
- Code: Select all
template <typename Function>
void ndThreadPool::ParallelExecute(const Function& ndFunction)
with a reference to that objects. and that function make a array of objects, the side of the number of threads, and initialize each one by the copy constructors.
if you open the function you will see that is using alloca a and them iterate over the array, calling the copy constructor on each class.
them for each class, it call the function operators with the thread ID and thread counts.
It takes a little tweaking and massaging but after you get it going the high level is very clean,
check it out, it could be a good exercise. this is as far adbavacne as it can get in C++, and it only works for c++ 11 and up.