Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I have a std::vector of template function pointers?

Tags:

c++

c++11

I have a template function that I want to store a pointer to inside a std::vector.

The function looks like this:

template<typename T> void funcName(T& aT, std::vector<std::string>& fileName){...}

Now I want to store multiple pointers to functions of this kind inside a std::vector. For non-template functions I would do it like this:

typedef std::vector<std::string> string_vt;
typedef void func_t(T&, string_vt&);
typedef func_t* funcPointer;
typedef std::vector<funcPointer> funcPointer_vt;

But what is the correct syntax for template functions? How can I store them?

EDIT: First of all, thank you for your fast response. This was my first Question on Stack Overflow, so I am sorry for not providing enough information.

The set of T is finite, it can either be of type ClassA or type classB. In these function templates I want to do changes to T (so either ClassA or ClassB) with some hard coded data. I have 8 of these functions, which basically initiate a default constructed T with data specific to the function. In my program, I want to initiate 2*8 default constructed T's (8 ClassA and 8 ClassB). Therefore I run a for loop, calling one function after the other, to initiate my T objects with the function's body data.

for(int i = 0; i < initT.size(); ++i){
   init_T[i]<T>(someT, fileName);
}

The for loop has as much iterations as there are function pointers inside the vector. At every iteration the function is called with some previously default constructed T and some other parameter. At the end the goal is to have 8 initiated T's with data specific to the function.

EDIT2: In case it helps, here is some actual source code. Inside the following function template I want to access my vector of function pointers in order to call the respective function.

template<typename T_Relation, typename T_Relation_Vec, bool row>
void bulk_load(initRelation_vt& aInitFunctions, T_Relation_Vec& aRel_Vec, const bool aMeasure, const uint aRuns, const char* aPath)
{
    for(size_t i = 0; i < aRuns; ++i)
    {
        MemoryManager::freeAll();
        aRel_Vec.clear();
        string_vt fileNames;
        for(size_t j = 0; j < aInitFunctions.size(); ++j)
        {
            aRel_Vec.emplace_back(T_Relation());
            aInitFunctions[j]<T_Relation>(aRel_Vec[j], fileNames);
            BulkLoader bl(fileNames[j].c_str(), tuples, aRel_Vec[j], delimiter, seperator);
            Measure lMeasure;
            if(aMeasure)
            {
                lMeasure.start();
            }
            try
            {   
                bl.bulk_load();
                if(row)
                {
                    BulkInsertSP bi;
                    bi.bulk_insert(bl, aRel_Vec[j]);
                }
                else
                {
                    BulkInsertPAX bi;
                    bi.bulk_insert(bl, aRel_Vec[j]);
                }
            }
            catch(std::exception& ex)
            {
                std::cerr << "ERROR: " << ex.what() << std::endl;
            }
            lMeasure.stop();
            if(aMeasure)
            {
                std::ofstream file;
                file.open (aPath, std::ios::out | std::ios::app);
                //print_result(file, flag, lMeasure.mTotalTime());
                file.close();
            }
        }
    }
}

This line is where the vector of function template pointers is accessed.

aInitFunctions[j]<T_Relation>(aRel_Vec[j], fileNames);
like image 251
N. Weber Avatar asked Jan 19 '17 19:01

N. Weber


2 Answers

Templates are an advanced technique for static polymorphism. In a typed language, like C++, without static polymorphism you would have to separately define every entity used and precisely indicate every entity referred to.

Mechanisms of static polymorphism in C++ allow to automate indication of function or method and defer it until build via overloading. It allows you to define multiple entities sharing some characteristics at once via templates and defer definition of particular specializations until build, inferred from use.

(Notice that in various scenarios, static polymorphism allows separate code, so that changes to use and to definition are independent, which is very useful.)

The important implication of this mechanism is that every specialization of your template may be of different type. It is unclear, as of when I'm responding, whether you want to store pointers to a single or multiple types of specialization in one type of container. The possibilities depend also on parameter and result types of the function template.

A function in C++ has a type that is a combination of list of its parameter types and its return type. In other words, two functions that take and return the same types are of the same type. If your function template neither took or returned template parameter type (ie. T) nor templated type (eg. std::vector<T>), every specialization of this function template would be taking and returning the same types and would therefore be a function of the same type.

    template <typename T>
    int func() { ... }

This (arguably useless) function template takes no arguments and returns int, whatever T is used to specialize the template. Therefore a pointer to it could be used wherever the parameter is defined as int (*f)(). In this case you could keep pointer to any specialization in one vector.

    typedef std::vector<std::string> string_vt;
    typedef int func_t();
    typedef func_t* funcPointer;
    typedef std::vector<funcPointer> funcPointer_vt;

    funcPointer x = &func<int>;
    funcPointer y = &func<float>;

As can be seen, every specialization of your function template is of the same type and both pointers fit in the same container.

Next case - what if function header depends on a template parameter? Every specialization would have a different signature, that is a different function type. The pointers to all of them would be of different types - so it wouldn't be possible to even typedef this pointer once.

    template <typename T>
    void func(std::vector<T> param) { ... }

In this case function template specialization is of different type depending on T used to specialize.

    typedef int func_t_int(std::vector<int>);
    typedef func_t_int* funcPointerInt;
    typedef std::vector<funcPointerInt> funcPointerInt_vt;

    typedef float func_t_float(std::vector<float>);
    typedef func_t_float* funcPointerFloat;
    typedef std::vector<funcPointerFloat> funcPointerFloat_vt;

    funcPointerInt x = &func<int>;

    funcPointerFloat x = &func<float>;

Specializations are of different types, because they take different type of vectors. Pointers do not fit in the same container.

It's mention-worthy at this point, that in this case it's not necessary to define every pointer type separately. They could be a template type:

    template <typename T>
    using funcPointer = void (*)(std::vector<T>);

Which now allows funcPointer<int> to be used as a type qualifier, in place of earlier funcPointerInt.

    funcPointer<float> y = &func<float>;

In more complicated situations a template could be created, whose every specialization is of a different type, and then would use a single instance of concrete vector to store various pointers to functions of type of only one of the specializations of your template. Although a simple template like in the example can only produce a single function per type, because every specialization yields one type of function and one function of that type, it's not impossible to conceive a scenario where various pointers to functions are obtained, both to specializations and usual functions, perhaps from various sources. So the technique could be useful.

But yet another scenario is that despite every specialization of the template being of different type, there's a need to store pointers to various specializations in single std::vector. In this case dynamic polymorphism will be helpful. To store values of different types, fe. pointers to functions of different types, in one type of variable, requires inheritance. It is possible to store any subclass in a field defined as superclass. Note however, that this is unlikely to accomplish anything really and probably not what you're really looking for.

I see two general possibilities now. Either use a class template with a method, which inherits from a non-template class.

    template <typename T>
    class MyClass : BaseClass
    {
    public:
        T operator()(const T& param, int value);
    }

    MyClass<int> a;
    MyClass<float> b;
    BaseClass* ptr = &a;
    ptr = &b;

While every specialization of this class may be of a different type, they all share superclass BaseClass, so a pointer to a BaseClass can actually point to any of them, and a std::vector<funcPointerBase> can be used to store them. By overloading operator() we have create an object that mimics a function. The interesting property of such a class is that it can have multiple instances created with parameter constructors. So effectively class template produces specializations of multiple types, and in turn every specialized class can produce instances of varying parametrization.

    template <typename T>
    class MyClass : BaseClass
    {
        int functor_param;
    public:
        MyClass(int functor_param);
        T operator()(const T& param, int value);
    }  

This version allows creation of instances that work differently:

    MyClass<int> a(1);
    MyClass<int> b(2);
    MyClass<float> c(4);
    MyClass<int>* ptr = &a;
    ptr = &b;
    ptr = &c;

I am no expert on functors, just wanted to present the general idea. If it seems interesting, I suggest researching it now.

But technically we're not storing function pointers, just regular object pointers. Well, as stated before, we need inheritance to use one type of variable to store values of various types. So if we're not using inheritance to exchange our procedural functions for something dynamically polymorphic, we must do the same to pointers.

    template <typename T>
    T func(std::pair < T, char>) {}

    template <typename T>
    using funcPointer = T(*)(std::pair<T, char>);

    template <typename T>
    class MyPointer : BasePointer
    {
        funcPointer<T> ptr;

    public:
        MyPointer(funcPointer<T> ptr);
        T()(std::pair <T, char>) operator*(std::pair <T, char> pair)
        {
            *ptr(pair);
        }
    };

This, again, allows creation of single std::vector<BasePointer> to store all possible pseudo-function-pointers.

Now the very important bit. How would You go about calling those, in either scenario? Since in both cases they are stored in a single std::vector<>, they are treated as if they were of the base type. A specific function call needs parameters of specific type and returns a specific type. If there was anything that all subclasses can do in the same way, it could be exposed by defining such a method in base class (in either scenario using functors or pointer..ors?), but a specific specialized function call is not that kind of thing. Every function call that You would want to perform in the end, after all this struggle, would be of a different type, requiring different type of parameters and/or returning different type of value. So they could never all fit into the same place in usual, not templated code, the same circumstances in execution. If they did, then dynamic polymorphism wouldn't be necessary to solve this problem in the first place.

One thing that could be done - which is greatly discouraged and probably defeats the purpose of dynamic polymorphism - is to detect subclass type at runtime and proceed accordingly. Research that, if you're convinced you have a good case for using this. Most likely though, it's probably a big anti-pattern.

But technically, anything you may want to do is possible somehow.

like image 128
szpanczyk Avatar answered Sep 20 '22 13:09

szpanczyk


If I have correctly understood you, I may have a really simple and efficient solution:

 template<class...Ts>
 struct functor{
   //something like a dynamic vtable
   std::tuple<void(*)(Ts&,std::vector<std::string>&)...> instantiated_func_ptr;
   template<class T>
   void operator ()(T& aT,std::vector<std::string>& fileName){
     get<void(*)(T&,std::vector<std::string>&)>(instantiated_func_ptr)
                                                          (aT,fileName);
   }
 };

Voilà!!

Until c++17, get<typename> is not defined so we have to define it (before the definition of the template functor above):

 template<class T,class...Ts>
 struct find_type{
    //always fail if instantiated
    static_assert(sizeof...(Ts)==0,"type not found");
 };
 template<class T,class U,class...Ts>
 struct find_type<T,U,Ts...>:std::integral_constant<size_t,
                              find_type<T,Ts...>::value+1>{};
 template<class T,class...Ts>
 struct find_type<T,T,Ts...>:std::integral_constant<size_t,0>{};
 template<class T,class...Ts>
 constexpr decltype(auto) get(const std::tuple<Ts...>& t){
   return get<find_type<T,Ts...>::value>(t);
 }

And an example to show how to use it:

 struct A{
    void show() const{
       std::cout << "A" << "\n";
       }
 };
 struct B{
    void show() const{
       std::cout << "B" << "\n";
       }
 };
 template<class T>
 void func1(T& aT,std::vector<std::string>& fileName){
   std::cout << "func1: ";
   aT.show();
 }
 template<class T>
 void func2(T& aT,std::vector<std::string>& fileName){
   std::cout << "func2: ";
   aT.show();
 }
 template<class T>
 void func3(T& aT,std::vector<std::string>& fileName){
   std::cout << "func3: ";
   aT.show();
 }
 using functorAB = functor<A,B>;
 int main(){
   auto functor1=functorAB{{func1,func1}};//equivalent to functorAB{{func1<A>,func1<B>}}
   auto functor2=functorAB{{func2,func2}};
   auto functor3=functorAB{{func3,func3}};
   auto v=std::vector<functorAB>{functor1,functor2,functor3};
   auto a=A{};
   auto b=B{};
   auto fileNames = std::vector<std::string>{"file1","file2"};
   for(auto& tf:v)
        tf(a,fileNames);  
   for(auto& tf:v)
        tf(b,fileNames);  
 }

In practice it is just a reproduction of the virtual call mechanism, the tuple in functor is kind of virtual table. This code is not more efficient than if you had written an abstract functor with virtual operator() for each of your class A and B and then implemented it for each of your functions... but it is much more concise, easier to maintain and may produce less binary code.

like image 35
Oliv Avatar answered Sep 20 '22 13:09

Oliv