I am creating a system where users can create applications by defining pieces of data (called resources) and functions (called systems) which act on the resources. I want the user to purely provide a function pointer of a certain type, and from that type I want to deduce which resources need to be passed to this function.
Here is some code for context:
#include <vector>
#include <unordered_map>
#include <memory>
using TypeID = size_t;
class Type
{
public:
template <class T>
static TypeID ID()
{
static TypeID id = s_Counter++;
return id;
}
private:
inline static TypeID s_Counter = 0;
};
struct Resource
{
virtual ~Resource() = default;
};
template <typename T>
struct Res : public Resource
{
inline static const TypeID ID = Type::ID<T>(); // Type::ID<T>() just returns a unique ID for every type
T Data;
};
class Application
{
private:
std::unordered_map<TypeID, std::unique_ptr<Resource>> m_Resources;
std::vector<void(*)()> m_Systems;
public:
template <typename T>
void AddResource()
{
m_Resources[Res<T>::ID] = std::make_unique<Res<T>>();
}
template <typename T>
T& GetResource()
{
return m_Resources[Res<T>::ID]->Data;
}
template <typename... Resources>
void AddSystem(void (*pSystem)(Resources...))
{
m_Systems.push_back([pSystem]() {
pSystem(/*Here, for every parameter in the parameter pack Resources,
I want to call GetResource<>() with the appropriate template parameter*/);
});
}
};
struct Foo
{
int a;
float b;
};
void system(Foo foo, int num, float val)
{
/*do stuff with foo, num and val*/
}
int main()
{
Application app;
app.AddResource<Foo>();
app.AddResource<int>();
app.AddResource<float>();
app.AddSystem(system);
}
In the AddSystem
function, I want to convert a list of template parameters such as <int, float, Foo>
into a list of function calls GetResource<int>(), GetResource<float>(), GetResource<Foo>()
so that the return values of these functions are then passed into the user-defined function pSystem
. This example should result in the line pSystem(GetResource<int>(), GetResource<float>(), GetResource<Foo>());
being generated.
Is there a portable way to do this? Such as a function the standard library provides?
If there is another obvious way of achieving the same result please let me know too, this design isn't set in stone yet.
To pass Get<Args>...
to the function pointer you can use pack expansion:
#include <iostream>
#include <utility>
void foo(int a,float b,double c) {
std::cout << a << b << c;
}
template <typename T> int get() { return 0; }
template <typename ...Args>
void bar(void(*f)(Args...)) {
f( get<Args>()...); // calls f(get<int>(),get<float>(),get<double>())
}
int main() {
bar(foo);
}