Compile-time selection of a function pointer

I would like to be able to select function pointers at compile time. Something like the ListAutomatic function in the following

int funk( int a, int b ) { return a * b / 2; }

template< typename T0, typename T1 >
int null_func( T0 a, T1 b ) { return 0; }

tuple< int( *)(int, int), int( *)(int, float) > functionList {
    funk,
    null_func<int, float>
};

// Pseudo code.
tuple< int( *)(int, int), int( *)(int, float) > functionListAutomatic {
    condition( funk_exist( funk( int, int ) )   , funk, null_func<int, int> ),
    condition( funk_exist( funk( int, string ) ), funk, null_func<int, string> ),
};

void main() {
    int res0 = get<0>( functionList )(1, 2);
    int res1 = get<1>( functionList )(1, 2);
}

      

I cannot figure out how to do this.

I know how to make funk_exist so that it is evaluated at compile time (I use a variant of this: https://en.wikibooks.org/wiki/More_C++_Idioms/Member_Detector ). But the two parameters funk and null_func are causing the problem. The compiler tries to find the function funk (int, string) and fails to evaluate funk_exist (). I need an expression that evaluates funk_exist () and then doesn't evaluate funk (int, string) if funk_exist () evaluates to false.

Appreciate your help.

+3


source to share


2 answers


namespace details {
  template<class...>struct voider{using type=void;};
  template<class...Ts>using void_t=typename voider<Ts...>::type;

  template<template<class...>class Z, class, class...Ts>
  struct can_apply:std::false_type{};
  template<template<class...>class Z, class...Ts>
  struct can_apply<Z,void_t<Z<Ts...>>,Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply=details::can_apply<Z,void,Ts...>;

      

injection cartridge! 1

Adding funk:

template<class...Ts>
funk_r = decltype(funk(std::declval<Ts>()...));
template<class...Ts>
can_funk = can_apply<funk_r, Ts...>;

      

now we know if we can function.

but who's the funk? funk_miester

:

template<class lhs, class rhs, class=void>
struct funk_miester {
  decltype(null_func<lhs,rhs>) operator()()const{
    return null_func<lhs, rhs>;
  }
};
template<class lhs, class rhs>
struct funk_miester<lhs, rhs, std::enable_if_t< can_funk<lhs,rhs>{} >> {
  funk_r<lhs,rhs>(*)(lhs, rhs) operator()()const{
    return [](lhs l, rhs r)->funk_r<lhs,rhs> {
      return funk(l,r);
    };
  }
};

      



and go down below this line to see the result:

tuple< int( *)(int, int), int( *)(int, float) > functionListAutomatic (
  funk_miester<int,int>{}(),
  funk_miester<int,string>{}()
);

      

and you are funkey.

Note that I check if it can be funk

called funk

in can_funk

, but you can replace it with whatever trait you want, as long as it generates a compile-time bool.

In my case, the lambda acts like an adapter, so if the signatures don't match, it will still generate a function pointer.


1 This just gives me a clue to determine if I can call funk

for some arguments. You have yours, so you don't need to use it.

0


source


I would like to thank Jakk for his answer. With a few tweaks, I got this to work and I have posted the complete program below.

However, there were several snafus.

Using the class template for Z in detail pushes the evaluation to detail and goes to funk_r. This means the compiler tries to evaluate funk (int, string), which then gives a compilation error. Apparently it is not possible to use SFINAE when using aliases, so I have not found a solution for this.



#pragma once
#include <string>
#include <tuple>
using namespace std;

int funk( int a, int b ) { return a * b; }

template< typename T0, typename T1 >
int null_funk( T0 a, T1 b ) { return 0; }

template< typename... Ts >
struct can_apply {
    using Yes = char[2];
    using  No = char[1];

    // SFINAE will take 'Yes' if funk( Us... ) matches. Otherwise it will take 'No'
    template< typename... Us >
    static Yes & test( decltype(funk( Us()... ))* );   // *1
    template< typename U0, typename... Us >
    static No  & test( U0* );

    static constexpr bool value = sizeof( test< Ts... >( nullptr ) ) == sizeof( Yes );
};

template< typename... Ts >
using funk_r = decltype(funk( declval<Ts>()... ));
template< typename... Ts >
using can_funk = can_apply< Ts... >;

template< typename lhs, typename rhs, typename = void >
struct funk_meister {
    typedef typename decltype(null_funk<lhs, rhs>( lhs(), rhs() ))(*TFunk)(lhs, rhs);
    TFunk operator()() const {
        return null_funk<lhs, rhs>;
    }
};
template<typename lhs, typename rhs>
struct funk_meister<lhs, rhs, enable_if_t< can_funk<lhs, rhs>::value > > {
    typedef typename funk_r<lhs, rhs>( *TFunk )(lhs, rhs);
    TFunk operator()() const {
        return []( lhs l, rhs r ) -> funk_r<lhs, rhs> {
            return funk( l, r );
        };
    }
};

tuple< int( *)(int, int),  int( *)(int, string),  int( *)(int, float)> functionList {
    funk_meister<int,int>{}(),  funk_meister<int,string>{}(),  funk_meister<int,float>{}() 
};

void test() {
    int   res0 = get<0>( functionList )(1, 2);
    int   res1 = get<1>( functionList )(1, "2");
    int   res2 = get<2>( functionList )(1, 2.5f);
}

      

* 1. You cannot use a template template here, because then the SFINAE score is output to funk_r <...>.

0


source







All Articles