cantordust
cantordust

Reputation: 1612

Calling template function with non-type template parameter using class attribute

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

Answers (3)

Axel Payan
Axel Payan

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

Jarod42
Jarod42

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

Thomas
Thomas

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

Related Questions