variadic template for class member undefined reference

How do I create a class member function that takes on multiple unknown number of arguments as parameters. I tried this implementation

In Behaviors.cpp
1
2
3
4
5
6
7
8
9
10
11
  template <typename ...ArgsT> void MacroBehavior::program(ArgsT... a_args){
 
        std::vector<std::shared_ptr<map_item> >  items {a_args...};
         
        for (auto i: items) {
            //items.push_back(i);
            i->address->owner=this->id;
            key_address.insert(std::make_pair(i->key, i->address));
        }

}


In Behaviors.h
1
2
3
4
5
6
7
8
9
10
11
12
MacroBehavior(Robot &o, std::string iid, int i_max_attempts);
    virtual ~MacroBehavior();
    
    void load_queue(int flag);
    virtual bool exec(int param);
    virtual void build_queue(int param)=0;
    
    void update(std::shared_ptr<map_item> f_it, std::shared_ptr<map_item> next, bool start_flag);
    
    template <typename ...ArgsT> void program(ArgsT... a_args);
};    
    


And I call the function with :

 
  this->program((*M)(S,f),(*OLU)(F,s),(*SU)(F,s));


The code compiles fine but at link time:

 
CV3-april-obs/Behaviors.h:311: undefined reference to `void MacroBehavior::program<std::shared_ptr<map_item>, std::shared_ptr<map_item>, std::shared_ptr<map_item> >(std::shared_ptr<map_item>, std::shared_ptr<map_item>, std::shared_ptr<map_item>)' 


Thanks
Chris
You can't put definitions of template functions in separate source files. The compiler needs them at the point of usage in order to figure out how to compile the usage.
Take all the contents of Behaviors.cpp and move them into Behaviors.h. If you want, you can #include "Behaviors.cpp" at the end of the header.
Including Behaviors.cpp resulted in a ton of recurse errors. I ended up moving the function definition for MacroBehavior::program from Behaviors.cpp to Behaviors.h and that fixed the problem.

Thanks
Chris
This is bugging me though. There's got to be a better way. I read that you can instantiate the template class in the header file where the definition resides and that should work but I cant find the appropriate syntax for this. Here is the code for map_item class for anyone interested in trying.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
struct name_pair {
    std::string name;
    std::shared_ptr<Behavior> address;
    };

   class map_item{
    public:
        map_item(std::shared_ptr<Behavior> add, std::string k) : address(add), key(k){ 
           
            for( auto& row : matrix )
                for( name_pair& item : row ) { item.name = "I" ; item.address = NULL; }
            
        };
        
        void print(){
            std::cout << "Key : " << key << std::endl; 
         
            for (int row=0; row<N; ++row)
                for (int col=0; col<N; ++col )
                   std::cout << row << ',' << col << "  " << matrix[row][col].name << '\n' ;
            
        }
        std::string key;
        std::shared_ptr<Behavior> address;
        static constexpr int N = 2 ;
        std::array< std::array<name_pair,N>, N > matrix ;
    };


Seems like the answer needs to be something like :
 
template void MacroBehavior::program<std::shared_ptr<map_item> >(std::shared_ptr<map_item>);

in Behaviors.h after definition of the template. The compiler accepts this but I still get the linker errors. I dont know how the ellipsis figures into this as well.

Thanks
Chris
You can instantiate a template explicitly, but you'll have to make an explicit instantiation for every different template parameter combination you use. For example, if you were implementing a map<K, V> and your program used map<std::string, int>, map<std::string, double, and map<int, double>, you'd need to explicitly instantiate each of those.
This is generally not done because it's more trouble than it's worth.
The only reason I am using the template is I thought it was a requirement to use variadic functions. I was not successful in converting the member function "MacroBehavior::program" into one that takes unknown multiple number of parameters without the use of the template ( unless I use an std::initializer). So there only really is one parameter type that needs instantiated and that's "std::shared_ptr<map_item>".

Is there a solution that does not involve using a variadic template for functions taking multlpe unknown parameter types?

If not, what would be the syntax of template instantiation for that one parameter type?

Thanks
Chris


So there only really is one parameter type that needs instantiated and that's "std::shared_ptr<map_item>".
No, because the function that takes 1 std::shared_ptr<map_item> is of a different type from the one that takes 2 and from the one that takes 3.

You could use a C variadic function, but variadic template functions were introduced because C variadic functions are so error-prone and dangerous to use.
I see. I think I may need to implement another variadic function but this time it needs to live in the cpp file because I run into some errors if the definition is in the header file. It works fine if the definition is separated from the declaration. I may only need 1 to 3 arguments to the function. I am still working on the details. Just in case the way I understand this is that I need to instantiate templates with one, then two, then three parameters. For the case of std::shared_ptr<map_item> then it would be :

1
2
3
4
typedef std::shared_ptr<map_item> m_item;
template void MacroBehavior::program<m_item >(m_item);
template void MacroBehavior::program<m_item >(m_item, m_item);
template void MacroBehavior::program<m_item >(m_item, m_item, m_item);


Is the syntax correct?

Thanks
Chris
Ok here is the actual class function I need to convert to variadic. I commented out the previous form:

1
2
3
4
5
6
7
8
9
10
11
template <typename ...ArgsT> std::shared_ptr<map_item> Behavior::operator()(run_type a, result_type b,  ArgsT... a_args){
   //std::shared_ptr<map_item>  Behavior::operator()(run_type a, result_type b, std::shared_ptr<Behavior> B){
          std::shared_ptr<map_item> temp=std::make_shared<map_item>(shared_from_this(), this->id);
          temp->matrix[a][b].name="O";
          map_items.push_back(temp);
          
          //std::shared_ptr<mail_envelope> tmail=std::make_shared<mail_envelope>(B, std::to_string(line_counter) );
         // mail_list.push(tmail);  //each Behavior maintains a mail list containing address and param of addressee
          
          return temp;
        }


And the template instantiation which compiles fine but has the undefined reference error at link time. Obviously I got the instantiation wrong.

1
2
   typedef std::shared_ptr<Behavior> b_item;
   template std::shared_ptr<map_item> Behavior::operator()<b_item>(run_type a, result_type b,  b_item);


Thanks
Chris
this time it needs to live in the cpp file because I run into some errors if the definition is in the header file. It works fine if the definition is separated from the declaration.
You mentioned that you get errors from recursion. Did you try removing the #include "Behaviors.h" from Behaviors.cpp when including it in the header?
I did try that after I read your suggestion. However, it results in a ton of multiple definitions for functions and classes in Behaviors.cpp. Probably bechase Behaviors.h is included by multiple files. Is there a solution?

Thanks
Chris

If Behaviors.cpp only includes template definitions there should be no duplicate definitions, because the compiler treats template definitions the same as if they were inline.
I created a Variadic.cpp that only has the definition of Behavior::operator()(Behavior::run_type, Behavior::result_type, ArgsV ...). I put in #include Variadic.cpp at the bottom of Behaviors.h. Then in Variadic.cpp I put at the top #include Behaviors.h. The result is :

1
2
3
4
5
6
7
Variadic.cpp:9:56: error: redefinition of ‘std::shared_ptr<map_item> Behavior::operator()(Behavior::run_type, Behavior::result_type, ArgsV ...)’
 template <typename ...ArgsV> std::shared_ptr<map_item> Behavior::operator()(run_type a, result_type b,  ArgsV... a_args){
                                                        
In file included from Behaviors.h:479:0,
                 from Variadic.cpp:7:
Variadic.cpp:9:56: error: ‘std::shared_ptr<map_item> Behavior::operator()(Behavior::run_type, Behavior::result_type, ArgsV ...)’ previously declared here
 template <typename ...ArgsV> std::shared_ptr<map_item> Behavior::operator()(run_type a, result_type b,  ArgsV... a_args){


I am going to investigate the dependencies of the headers in my project. I just read last night that headers should be independently compileable. Maybe after I look into that, this solution might work?

Thanks
Chris
Omigosh!

Header guards in Variadic.cpp made the redefinition warnings go away! Hopefully I dont have any runtime issues. I will have to check it later.

Thanks
Chris

Works, no runtime issues.

Thanks for your time and interest
Chris
Topic archived. No new replies allowed.