0

Turns out I need to build a function that takes a variable number of arguments of no predetermined types,something like:

myFun(int param1, char param2, short param3....);

And I thought about using variadic template functions. This is actually a member function of a class which is inside a namespace, this is my header file:

//USART.hh file
namespace usartNameSpace
{
    class USART
    {
        public:
        USART();
        ~USART();
        template <typename firstArgument, typename ...Arguments> bool parseUserCommands(firstArgument &argument, Arguments &... args);
    };
}
  

This is the implementation(I have omitted both the constructor and destructor)

//USART.cpp file
using namespace usartNameSpace;
template <typename firstArgument, typename ...Arguments> bool USART::parseUsercommands(firstArgument &argument, Arguments&... args)
{

    //stuff the function does
}

I will say it again, my goal is to be able to create a method that takes a variable number of parameters of no particular type, with this conception I think I'm able to reach that, however, when calling the method from the main.cpp file, I get the following error at compile time:

undefined reference to `bool usartNameSpace::USART::parseUserCommands<int, char>(int&, char&)'
collect2: error: ld returned 1 exit status 
make: *** [makefile:68: mainProject.elf] Error 1

I do not really know what is wrong with my conception of things here, someone told me to try to put the implementation in the same header where the definition lies but that did not work neither(If someone could explain this one also, it would be very nice).

Lastly, here is my main file:

#include "USART.hh"

usartNameSpace::USART coms(USART2);


int n = 8;
char m = 'a';
int main(void)
{
    coms.parseUserCommands<int, char>(n, m);
    return 0;
}
Aamir
  • 1,974
  • 1
  • 14
  • 18
Juan_David
  • 126
  • 2
  • 11
  • 2
    Does this answer your question? [Why can templates only be implemented in the header file?](https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) – tkausl Aug 07 '22 at 00:53
  • @tkausl Hello, thanks for answering. Even though this solves part of my questioning, still is not what fully concerns my problem since my class is not a template class. Yet still thanks a lot. – Juan_David Aug 07 '22 at 01:02
  • `Even though this solves part of my questioning, still is not what fully concerns my problem since my class is not a template class.` This applies to __all__ templates, not just class templates. – tkausl Aug 07 '22 at 01:03
  • You said `someone told me to try to put the implementation in the same header where the definition lies but that did not work neither` so _how_ did it not work in this case? – tkausl Aug 07 '22 at 01:04

1 Answers1

-1

Template Definition vs Declaration TL;DR

Put the definition of template functions, classes and methods (if isolated to a single class method) in the header file. Anything else can be defined in a source file.

Longer

The problem is that is not possible to have templates definitions (classes, functions and methods) in source files as the compiler need to know the instantiated type when it compiles the file, it can't leave it as a template parameter. In this case you have to put the definition of the template method with its declaration in the header. However, this doesn't mean you have to put non-template method's definitions in the same header, you can even create methods with the same name but aren't templated and split the declaration and definition (as far as my brief testing went).

Variadic Template Use

The pattern you appear to be using is a recursive template parameter function (method in this case but works for both). The key is that this is a recursive pattern thus you need a base method that just takes one (in this case template) argument. The recursive function takes a template initial argument and a second variadic parameter pack. You do what you want with the first argument, then call the method again parsing the variadic arguments (args... with the ellipsis). This will unfold the pack until you it the base class.

Another method would be to take a tuple of variadic arguments and manipulate the tuple so you can access all of the elements in a single function call, removing the need for a recursive variadic call pattern.

You could also (I believe) use std::make_tuple on the variadic pack to make it into a std::tuple and in that case you wouldn't need recursive pattern or the firstArgument parameter, just a function that take Args... args but I didn't explore that in testing. Some homework for you.

Code

Note: This is the code that was tested (briefly). I didn't explore in depth the solution with tuples but provided resources below that will hopefully guide you.

/// USART.hh
/// \note added header guards for completeness

#ifndef USART_H  ///< header guard
#define USART_H

#include <iostream>
#include <string>

namespace usartNameSpace
{
    class USART
    {
    public:
        
        USART();

        ~USART();
        
        /// Base case for recursive call
        template<typename FirstA> 
        bool parseUserCommands(FirstA first)
        {
            std::cout << first << std::endl;
            return true;
        }

        /// Variadic Resursive call
        template<typename FirstA, typename... Args> 
        bool parseUserCommands(FirstA first, Args... args)
        {
            std::cout << first << std::endl;
            return parseUserCommands(args...);
        }

        /// Non-template method with the same name as a templated method
        bool parseUserCommands();

        /// Non template method
        void printer(std::string str);
    };
}

#endif // USART_H
/// USART.cpp

#include <USART.hh>

namespace usartNameSpace
{

        /// Implement all non-template methods in source file (*.cpp)

        USART::USART()
        { std::cout << "USART constructor" << std::endl; }
        
        USART::~USART()
        { std::cout << "USART destructor" << std::endl; }

        bool USART::parseUserCommands()
        {
            std::cout << "No arguments" << std::endl;
            return true;
        }

        void USART::printer(std::string str)
        { std::cout << str << std::endl; }
}
/// main.main.cpp

#include <USART.hh>
#include <iostream>

auto main() -> int
{
    auto n = 8;
    auto c = 'a';
    auto s = "Hello";
    auto b = true;

    usartNameSpace::USART usart;

    usart.parseUserCommands(n, c);          ///< Two arguments
    usart.parseUserCommands(s);             ///< One argument
    usart.parseUserCommands(n, c, s, b);    ///< Four arguments

    usart.printer("Bye!");                  ///< Non-template method
    usart.parseUserCommands();              ///< Non-template method of same name as templated one

    return 0;
}
# Output
# Build and run instructions included for completeness
# I can provide a manual build command if requested.

$ bpt build -t :c++20:gcc-11 -o build

$ ./build/main
USART constructor
8
a
Hello
8
a
Hello
1
Bye!
No arguments
USART destructor

Compilers and Tools

  • GCC-11.3.0
  • bpt build system

Links and Resources Used

oraqlle
  • 186
  • 2
  • 12