ThreeStarProgrammer57
ThreeStarProgrammer57

Reputation: 3054

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?

Upvotes: 2

Views: 323

Answers (2)

n. m. could be an AI
n. m. could be an AI

Reputation: 120229

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);
}

Upvotes: 3

max66
max66

Reputation: 66230

As explained in comments, there isn't a single pointer to a template function (or method) because isn't a function but a set of functions.

The best I can imagine to do something similar (I mean... explicating the Unit type inside GetMaterial()) is to add a couple of sub-structs in Impl with static template methods

   struct lm1
    {
      template <Unit U>
      static Material * func (Point const & p)
       { return nullptr; }
    };

   struct lm2
    {
      template <Unit U>
      static Material * func (Point const & p)
       { return nullptr; }
    };

then rewrite GetMaterial() as follows

template <typename T>
Material * GetMaterial (Point const & p)
 { return T::template func<Unit::model_unit>(p); }

and use it this way

void DoSomething()
 {
   Point p = {};

   auto mat_1 = GetMaterial<lm1>(p);
   auto mat_2 = GetMaterial<lm2>(p);
 }

This way you pass to GetMaterial() a single type (lm1 or lm2) that contains the full set of template functions; then, inside GetMaterial(), you select the right function explicating Unit::model_unit.

The following is a full working example

enum Unit { model_unit, nanometers, meters };

struct Material
 { double rho; };

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

struct Impl
 {
   struct lm1
    {
      template <Unit U>
      static Material * func (Point const & p)
       { return nullptr; }
    };

   struct lm2
    {
      template <Unit U>
      static Material * func (Point const & p)
       { return nullptr; }
    };


   template <typename T>
   Material * GetMaterial (Point const & p)
    { return T::template func<Unit::model_unit>(p); }

   void DoSomething()
    {
      Point p = {};

      auto mat_1 = GetMaterial<lm1>(p);
      auto mat_2 = GetMaterial<lm2>(p);
    }
 };

int main ()
 {
   Impl i;

   i.DoSomething();
 }

Upvotes: 3

Related Questions