Reputation: 1612
Suppose you have an enum class
called Func
, a class with a Func
attribute and a Functions
class containing a function template compute
:
enum class Func
{
Sin,
Cos
// Others...
}
class Functions
{
public:
template<Func fn>
static inline double compute(const std::vector<double>& _args);
}
template<>
inline double compute<Func::Sin>(const std::vector<double>& _args)
{
return std::sin(std::accumulate(_args.begin(), _args.end(), 0.0));
}
template<>
inline double compute<Func::Cos>(const std::vector<double>& _args)
{
return std::cos(std::accumulate(_args.begin(), _args.end(), 0.0));
}
// More specialisations...
class Foo
{
private:
Func func;
double result;
public:
inline void compute(const std::vector<double>& _args)
{
result = Functions::compute<func>(_args);
// A few other operations follow.
}
}
I would like to call the appropriate specialised template from Functions
by passing the func
attribute. However, this doesn't work because func
is not known at compile time. I get the following error in clang
:
candidate template ignored: invalid explicitly-specified argument for template parameter 'fn'
^
I believe that I can achieve this by making the compute
method in Foo
a template as well and specialising for each Func
:
class Foo
{
private:
Func func;
double result;
public:
template<Func fn>
inline void compute(const std::vector<double>& _args)
{
result = Functions::compute<fn>(_args);
// A few other operations follow.
}
}
template<>
inline void compute<Func::Sin>()(const std::vector<double>& _args)
{
result = Functions::compute<Func::Sin>(_args);
}
template<>
inline void compute<Func::Cos>()(const std::vector<double>& _args)
{
result = Functions::compute<Func::Cos>(_args);
}
// Etc.
However, this defeats the purpose because I would like to use the func
attribute in Foo
. Also, I would like to avoid the double handling because Foo::compute()
performs a few more operations, which I would have to copy into each specialisation.
Is there any way to achieve this functionality by using the func
attribute?
Upvotes: 1
Views: 383
Reputation: 99
You cannot solve this using templates. Template resolution append and compile time and you want to chose an run-time. There are a lot of solutions such as the ones proposed but none of them is going to make usage of template.
Upvotes: 1
Reputation: 217275
You don't have to specialize Foo::compute
. You may directly use your generic definition:
template<Func fn>
void compute(const std::vector<double>& _args)
{
result = Functions::compute<fn>(_args);
// A few other operations follow.
}
If you want to turn your runtime member into compile time value, you have to dispatch yourself though. switch
or array are possible way
class Foo
{
private:
Func func;
double result;
public:
void compute(const std::vector<double>& _args)
{
switch (func) {
case Func::Sin: result = Functions::compute<Func::Sin>(_args); break;
case Func::Cos: result = Functions::compute<Func::Cos>(_args); break;
// ...
}
// A few other operations follow.
}
};
Upvotes: 1
Reputation: 181745
Template resolution is a fully compile-time mechanism. In this case, you need some way to select at runtime which function is called: function pointers, virtual functions, a switch statement...
Example using function pointers:
typedef double (*Func)(const std::vector<double>&);
double Sin(const std::vector<double>& args) {
return std::sin(args[0]);
}
// etc.
class Foo
{
private:
Func func;
double result;
public:
inline void compute(const std::vector<double>& _args)
{
result = func(_args);
// A few other operations follow.
}
}
Upvotes: 3