Search code examples
c++templatesmetaprogramming

Using template parameters to create a list of arguments which are passed to another function


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.


Solution

  • 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);
    }