Llopeth
Llopeth

Reputation: 406

Explicit instantiation for a set of types

In my code usually I have to write a function that takes a "Path-like" type, that is, something that I can convert to boost::filesystem::path, for example

in A.hpp

struct A
{
  template <typename PathLike> 
  void myFunction(PathLike path);
};

in A.cpp

template <typename PathLike> 
void A::myFunction(PathLike path)
{
   boost::filesystem::Path p = convertToBoostPath(path);
   //do something...
}

//Explicit instantiations needed 
template void A::myFunction(string);  
template void A::myFunction(QString);
template void A::myFunction(char const *);
//....

The problem is that if I want to do the same thing in a different function B, I need to add the explicit instantiations all over again. Maybe I'm taking a wrong approach.

Upvotes: 2

Views: 116

Answers (3)

Jarod42
Jarod42

Reputation: 217075

How about create a class PathLike and use it:

class PathLike
{
public:
    explicit PathLike(const QString& path) : mPath(convertToBoostPath(path)) {}
    explicit PathLike(const std::string& path) : mPath(convertToBoostPath(path)) {}
    explicit PathLike(const char* path) : mPath(convertToBoostPath(path)) {}
    PathLike(const boost::filesystem::path& path) : mPath(path) {}

    const boost::filesystem::path& get() const { return mPath;}
    operator const boost::filesystem::path&() const { return mPath;}
private:
    boost::filesystem::path mPath;
};

(I mark other constructor explicit to promote filesystem::path, but up to you to add/remove explicit).

And then:

struct A
{
    void myFunction(PathLike path)
    {
       boost::filesystem::path p = path;
       //do something...
    }
};

Upvotes: 1

Maxim Egorushkin
Maxim Egorushkin

Reputation: 136208

One way to explicitly instantiate a function template for a pack of types is to take the address of each function instantiation. E.g.:

template<class... Ts>
void instantiate_myFunction() {
    auto f = [](auto&&) { return 1; };
    auto initializer_list = { f(&A::myFunction<Ts>)...  };
    static_cast<void>(initializer_list);
}

int main() {
    instantiate_myFunction<std::string, char const*>();
}

Upvotes: 0

Max Langhof
Max Langhof

Reputation: 23681

Instead of writing a template function that takes any PathLike and also does the real work, write a template function that takes any PathLike, converts it to boost::filesystem::Path as shown, then calls a non-template function (whose definition can be in the .cpp) which will do the real work.

In A.hpp:

class A
{
public:
  template <typename PathLike> 
  void myFunction(PathLike path);

private:
  void myFunctionImpl(boost::filesystem::Path path);
};


template <typename PathLike> 
void A::myFunction(PathLike path)
{
  myFunctionImpl(convertToBoostPath(path));
}

In A.cpp:

void A::myFunctionImpl(boost::filesystem::Path path)
{
  // do something...
}

This has the added benefit that misuse of the interface results in compiler errors, not linker errors.

Upvotes: 3

Related Questions