Search code examples
c++templatestemplate-templates

How to pass a template template non-type member function pointer?


I would like to pass (to a template member function) a pointer to a another template member function as a template template non-type parameter.

Here is what I've tried:

enum Unit { model_unit, nanometers, meters };

struct Material {
     double rho;
};

struct Point {
    double x, y, z;
};

struct Impl{

    template<Unit unit>
    Material * LookupMat_1(const Point& p) {
        return nullptr; // let's suppose it returns a valid pointer
    }

    template<Unit unit>
    Material * LookupMat_2(const Point& p) {
        return nullptr; // let's suppose it returns a valid pointer
    }

    // compiler error here:
    // expected 'class' or 'typename' before 'Material'
    // template<template<Unit> Material * (Impl::*LookupFunc)(const Point&)
    //                         ^~~~~~~~
    template<template<Unit> Material * (Impl::*LookupFunc)(const Point&) >
    Material * GetMaterial(const Point & p) {

        return (this->*LookupFunc<Unit::model_unit>)(p);
    }

    void DoSomething() {

        Point p = {};

        auto mat_1 = GetMaterial<LookupMat_1>(p);
        auto mat_2 = GetMaterial<LookupMat_2>(p);
    }
};

int main() {

    Impl i;

    i.DoSomething();

}

My syntax is wrong, the compiler says:

main.cpp:25:29: error: expected 'class' or 'typename' before 'Material'
template<template<Unit> Material * (Impl::*LookupFunc)(const Point&)
                        ^~~~~~~~

I cannot figure out the right syntax.

LookupFunc is a template of type Material * (Impl::*)(const Point&) which is a pointer to a member fonction.

Is what I am trying to do possible?

What am I missing?


Solution

  • Here's the class-template-as-functor way of doing this.

    template<Unit unit>
    struct LookupMat_1
    {
       Material * operator()(const Point& p) {
           return nullptr;
       }
    };
    
    template<Unit unit>
    struct LookupMat_2
    {
       Material * operator()(const Point& p) {
           return nullptr;
       }
    };
    
    template<template<Unit> typename LookupMat>
    Material * GetMaterial(const Point & p)
    {
        return LookupMat<Unit::model_unit>()(p);
    }