函数指针的编译时选择
Compile-time selection of function pointer
我想要函数指针的编译时选择。
类似于下面的 functionListAutomatic
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);
}
我不知道该怎么做。
我确实知道如何制作 funk_exist 以便它在编译时求值(我使用它的变体:https://en.wikibooks.org/wiki/More_C++_Idioms/Member_Detector)。但是 funk 和 null_func 这两个参数会导致问题。编译器试图找到一个 funk(int, string) 函数,但在计算 funk_exist() 之前失败了。我需要一个评估 funk_exist() 的表达式,然后 not 评估 funk( int, string ) 如果 funk_exist() 评估为 false.
感谢您的帮助。
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...>;
样板注入!1
添加放克:
template<class...Ts>
funk_r = decltype(funk(std::declval<Ts>()...));
template<class...Ts>
can_funk = can_apply<funk_r, Ts...>;
现在我们知道我们是否可以放克了。
但是谁会害怕呢? 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);
};
}
};
并在这条线下方查看结果:
tuple< int( *)(int, int), int( *)(int, float) > functionListAutomatic (
funk_miester<int,int>{}(),
funk_miester<int,string>{}()
);
而且你很有趣。
请注意,我检查是否可以在 can_funk
中调用 funk
,但您可以将其替换为您想要的任何特征,只要它生成编译时布尔值即可。
在我的例子中,lambda 充当适配器,因此如果签名不匹配,它仍会生成一个函数指针。
1 这只是给了我一个特征来检测我是否可以在某些参数上调用 funk
。你有自己的,所以你不必使用它。
感谢 Yakk 的回答。通过一些修改,我让它工作了,我已经在下面发布了完整的程序。
但是有一些混乱。
在细节中使用 class Z 模板将评估从细节推到 funk_r。这意味着编译器尝试评估 funk(int, string),然后给出编译错误。显然,在使用 using 制作别名时不可能使用 SFINAE,所以我没有找到解决方案。
#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。无法在此处使用 class 模板,因为 SFINAE 评估会被推送到 funk_r<...>。
我想要函数指针的编译时选择。 类似于下面的 functionListAutomatic
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);
}
我不知道该怎么做。
我确实知道如何制作 funk_exist 以便它在编译时求值(我使用它的变体:https://en.wikibooks.org/wiki/More_C++_Idioms/Member_Detector)。但是 funk 和 null_func 这两个参数会导致问题。编译器试图找到一个 funk(int, string) 函数,但在计算 funk_exist() 之前失败了。我需要一个评估 funk_exist() 的表达式,然后 not 评估 funk( int, string ) 如果 funk_exist() 评估为 false.
感谢您的帮助。
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...>;
样板注入!1
添加放克:
template<class...Ts>
funk_r = decltype(funk(std::declval<Ts>()...));
template<class...Ts>
can_funk = can_apply<funk_r, Ts...>;
现在我们知道我们是否可以放克了。
但是谁会害怕呢? 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);
};
}
};
并在这条线下方查看结果:
tuple< int( *)(int, int), int( *)(int, float) > functionListAutomatic (
funk_miester<int,int>{}(),
funk_miester<int,string>{}()
);
而且你很有趣。
请注意,我检查是否可以在 can_funk
中调用 funk
,但您可以将其替换为您想要的任何特征,只要它生成编译时布尔值即可。
在我的例子中,lambda 充当适配器,因此如果签名不匹配,它仍会生成一个函数指针。
1 这只是给了我一个特征来检测我是否可以在某些参数上调用 funk
。你有自己的,所以你不必使用它。
感谢 Yakk 的回答。通过一些修改,我让它工作了,我已经在下面发布了完整的程序。
但是有一些混乱。
在细节中使用 class Z 模板将评估从细节推到 funk_r。这意味着编译器尝试评估 funk(int, string),然后给出编译错误。显然,在使用 using 制作别名时不可能使用 SFINAE,所以我没有找到解决方案。
#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。无法在此处使用 class 模板,因为 SFINAE 评估会被推送到 funk_r<...>。