I am working on a factory that can register and create classes with a different set a parameter types and numbers. I search over the internet and I managed to create this class:
template< typename Key, typename BaseClass >
class Factory {
static_assert( std::has_virtual_destructor< BaseClass >::value,
"BaseClass must have a virtual destructor" );
public:
template< typename DerivedClass, typename ... Args >
void register_creator( const Key& key )
{
static_assert( std::is_base_of< BaseClass, DerivedClass >::value,
"DerivedClass must be a subclass of BaseClass" );
static_assert( std::is_constructible< DerivedClass, Args... >::value,
"DerivedClass must be constructible with Args..." );
creators_.emplace(
CreatorKey { key, create_function_type_index< Args... >() },
reinterpret_cast< CreateFunc< > >( create_function_impl<
DerivedClass, Args... > ) );
}
template< typename ... Args >
std::unique_ptr< BaseClass > create(
const Key& key,
Args&&... args ) const
{
auto creator = creators_.find(
{ key, create_function_type_index< Args... >() } );
if( creator != creators_.end() ) {
return reinterpret_cast< CreateFunc< Args... > >( creator->second )(
std::forward< Args>( args )... );
} else {
return {};
}
}
private:
template< typename ... Args >
static std::type_index create_function_type_index()
{
return {typeid( CreateFunc<Args...> )};
}
template< typename DerivedClass, typename ... Args >
static std::unique_ptr< BaseClass > create_function_impl(
const Args&... args )
{
return std::unique_ptr< BaseClass > { new DerivedClass {
std::forward< const Args& >( args )... } };
}
template< typename ... Args >
using CreateFunc = typename std::add_pointer< std::unique_ptr< BaseClass >( const Args&... ) >::type;
using CreatorKey = std::pair< Key, std::type_index >;
std::map< CreatorKey, CreateFunc< > > creators_;
};
My goal is to be able to run this kind of code:
class ___A___ {};
class ___B___ {};
class ___Base___ {
public:
virtual ~___Base___() = default;
protected:
___Base___( ___A___ a, ___B___ b ) : a_( a ), b_( b ) {
}
protected:
___A___ a_;
___B___ b_;
};
class ___Derived___: public ___Base___ {
public:
___Derived___( ___A___& a, ___B___& b ) : ___Base___( a, b ) {
}
};
class ___Derived2___: public ___Base___ {
public:
___Derived2___( ___A___ a, ___B___ b ) : ___Base___( a, b ) {
}
};
class ___Derived3___: public ___Base___ {
public:
___Derived3___( ___A___& a, ___B___ b ) : ___Base___( a, b ) {
}
};
Factory< std::string, ___Base___ > factory;
factory.register_creator< ___Derived___, ___A___ &, ___B___& >( "Derived" );
factory.register_creator< ___Derived2___, ___A___, ___B___ >( "Derived2" );
factory.register_creator< ___Derived3___, ___A___ &, ___B___ >( "Derived3" );
___A___ a;
___B___ b;
auto D = factory.create( "Derived", a, b );
auto D2 = factory.create( "Derived2", a, b );
auto D3 = factory.create( "Derived3", a, b );
The registers works perfectly for both reference and value parameters but I cannot manage to instiate them using the creators. During my debugging, I saw that all the parameters where given by reference and never by value.
Using deduced types like this:
auto D = factory.create( "Derived", a, b );
in order to do a cast is something you should never do. The type deduces here will be basically &decltype(a) (not decltype(&a); a reference to the type of a, not the type of a pointer to a).
return reinterpret_cast< CreateFunc< Args... > >( creator->second )(
std::forward< Args>( args )... );
this requires exact type matching. When this matches the type, it happens accidentally more than anything.
To do what you want, you have a whole pile of work to do, even if you want to only support "exact matching" of types.
Given a type T, there are a number of ways it can be used as an argument to a function.
It can be moved into the argument: T&&. A "sink" argument.
It can be duplicated into the argument: T. A "value" argument.
It can be referred to by the argument but guaranteed not to be modified, T const&. An "in" argument.
It can be referred to by the argument, and possibly modified, T&. An "in/out" argument.
The other options are possible, but are less interesting.
At the point where you register your function, you have to work out both the type T and which of the 4 above cases you are dealing with.
Then, at the point where you want to call your function, you have to work out the type T and which of the above 4 you support.
Then you have to inject the code to glue the incoming argument to the function to call.
We can simplify this by having fewer kinds of argument than the above 4 relatively commonly used in C++ code. For example, "value" can usually replace "sink" at the cost of one extra std::move construction.
If you don't want the caller to have to know exactly what kind of protocol the called type needs, you have to type erase the details.
Imagine an adapter:
template<class T>
struct arg_adapter;
it can be constructed from a T&&, T const& or T&. When it is constructed, it records a void* pointer to the incoming object, and which of the 3 categores of type it was passed.
It has a .get<U> method, where U can be any of T&&, T const& or T&. It checks if the .get is compatible; if it is, it casts the void* stored within to remove_reference_t<U>*, dereferences it, and static_cast<U>'s it.
If it fails, it throws an exception or calls exit(-1) or somesuch.
Now you store std::function< Out(arg_adapter<std::decay_t<Args>>...) > instead of std::function< Out(Args...) >.
You know, something like this:
template
struct tag_t {};
template<class Dest>
using p_getter = Dest(*)(void*);
template<class T>
struct arg_vtable {
p_getter<T&> ref = 0;
p_getter<T const&> cref = 0;
p_getter<T> value = 0;
p_getter<T&&> move = 0;
};
template<class Dest, class Src>
p_getter<Dest> make_getter() {
return [](void* ptr)->Dest{
return (Src&&)(*static_cast<std::decay_t<Src>*>(ptr));
};
}
template<class T>
arg_vtable<T> make_arg_vtable( tag_t<T const&> ) {
return {
0,
make_getter<T const&, T const&>(),
make_getter<T, T const&>(),
0
};
}
template<class T>
arg_vtable<T> make_arg_vtable( tag_t<T&> ) {
return {
make_getter<T&, T&>(),
make_getter<T const&, T&>(),
make_getter<T, T&>(),
0
};
}
template<class T>
arg_vtable<T> make_arg_vtable( tag_t<T&&> ) {
return {
0,
make_getter<T const&, T&&>(),
make_getter<T, T&&>(),
make_getter<T&&, T&&>(),
};
}
template<class T>
arg_vtable<T> make_arg_vtable( tag_t<T const&&> ) {
return make_arg_vtable( tag_t<T const&>{} );
}
template<class T, class U>
arg_vtable<T> make_arg_vtable( tag_t<T volatile&&> ) {
return make_arg_vtable( tag_t<T&&>{} );
}
template<class T, class U>
arg_vtable<T> make_arg_vtable( tag_t<T volatile&> ) {
return make_arg_vtable( tag_t<T&>{} );
}
template<class T, class U>
arg_vtable<T> const* get_arg_vtable( tag_t<U> tag ) {
static const arg_vtable<T> retval = make_arg_vtable<T>(tag);
return &retval;
}
template<class T>
struct arg_adapter {
arg_vtable<T> const* vtable = 0;
void* pvoid = 0;
template<class U>
arg_adapter( U&& u ):
vtable( get_arg_vtable<T>( tag_t<U&&>{} ) ),
pvoid( (void*)std::addressof(u) )
{}
T get(tag_t<T>) {
if (!vtable->value) throw std::invalid_argument("value");
return vtable->value(pvoid);
}
T&& get(tag_t<T&&>) {
if (!vtable->move) throw std::invalid_argument("move");
return vtable->move(pvoid);
}
T& get(tag_t<T&>) {
if (!vtable->ref) throw std::invalid_argument("ref");
return vtable->ref(pvoid);
}
T const& get(tag_t<T const&>) {
if (!vtable->ref) throw std::invalid_argument("cref");
return vtable->cref(pvoid);
}
};
template<class R, class...Args>
using adapt_function = std::function< R(arg_adapter<std::decay_t<Args>>...) >;
template<class R, class...Args, class F>
adapt_function<R, Args...> adapt_args( F&& f ) {
return [f=std::forward<F>(f)](arg_adapter<std::decay_t<Args>>... args)->R{
return f( args.get( tag_t<Args>{} )... );
};
}
using func_token = std::shared_ptr<void>;
template<class R, class...Args>
R invoke_token( func_token f, Args&&... args ) {
auto const* pf = static_cast<adapt_function<R, Args...>*>(f.get());
return (*pf)( std::forward<Args>(args)... );
}
template<class R>
struct factories {
std::map< std::string, func_token > funcs;
template<class...Args, class F>
void add( std::string s, F&& f ) {
funcs[s] = std::make_shared<adapt_function<R,Args...>>(adapt_args<R,Args...>(std::forward<F>(f)));
}
template<class...Args>
R invoke( std::string s, Args&&... args ) {
auto it = funcs.find(s);
if (it==funcs.end()) throw std::invalid_argument("s");
return invoke_token<R>( it->second, std::forward<Args>(args)... );
}
};
Live example
It needs forwarding overloads to arg_adapter::get like the ones for make_arg_vtable I suspect.
Overall, I think this is a bad idea, because the invokers only handle cv conversion and nothing else. Anything else causes a crash or other undefined behavior.
Related
I have a number of C++ structs with a number of methods. The C++ structs have a
"default" instance, and I would like to expose a "c" wrapper functions that uses
this default instance. But I would also like to avoid repeating all the
prototyles.
Alkind of C++11/14/17 and/or macro tricks are welcome, but I do not want to use
code-generators.
I have something that almost works, but I'm still struggling with a few
details.
// C++ class that have a "default-instance" ///////////////////////////////////
struct Foo {
int a() { return 1; }
int b(int) { return 2; }
int c(int, int) { return 3; }
};
Foo *FOO = nullptr;
// emulating existing c code that can not be changed //////////////////////////
typedef int (*ptr_a_t)();
ptr_a_t ptr_a = nullptr;
typedef int (*ptr_b_t)(int);
ptr_b_t ptr_b = nullptr;
typedef int (*ptr_c_t)(int, int);
ptr_c_t ptr_c = nullptr;
// Wrapper code (almost generic) //////////////////////////////////////////////
template <typename T, T>
struct Proxy;
// Wrapper class that will use the defualt instance if initialized (FOO is
// hardcoded).
template <typename T, typename R, typename... Args, R (T::*mf)(Args...)>
struct Proxy<R (T::*)(Args...), mf> {
static R call(Args... args) {
if (FOO) {
// ^^^
return ((*FOO).*mf)(args...);
// HARD-CODED ^^^^
} else {
return -1;
}
}
};
// Helper function to deduce the Proxy-class (method 'b' is hardcoded)
template <typename T, typename R, typename... Args>
auto deduce_args(R (T::*mf)(Args...)) -> Proxy<R (T::*)(Args...), &T::b> {
// HARD-CODED ^
return Proxy<R (T::*)(Args...), &T::b>();
// HARD-CODED ^
}
// Wrap the methods ////////////////////////////////////////////////////////
//#define wrap_a decltype(deduce_args(&Foo::a))::call
#define wrap_b decltype(deduce_args(&Foo::b))::call
//#define wrap_c decltype(deduce_args(&Foo::c))::call
int main() {
// Test that it works
//ptr_a = &wrap_a; // does not work due to hard-coded method
ptr_b = &wrap_b;
//ptr_c = &wrap_c; // does not work due to hard-coded method
return ptr_b(0);
}
I can live with the hard-coded "FOO" in the proxy, as I only need one proxy per class, but it would be cool if the instance pointer could be passed as a
template argument.
The hard-coded method in "deduce_args" is really anoying, how can I eliminate
that??
Is there a better way to do this (the function pointers can not be replaced with std::function).
Using C++14 alias turned out to be a much easier way of achieving what I wanted.
// compile using the "-std=c++14" flag
// C++ class that have a "default-instance" ///////////////////////////////////
struct Foo {
int a() { return 1; }
int b(int) { return 2; }
int c(int, int) { return 3; }
};
Foo *FOO = nullptr;
// emulating existing c code that can not be changed //////////////////////////
typedef int (*ptr_a_t)();
ptr_a_t ptr_a = nullptr;
typedef int (*ptr_b_t)(int);
ptr_b_t ptr_b = nullptr;
typedef int (*ptr_c_t)(int, int);
ptr_c_t ptr_c = nullptr;
// Wrapper code ///////////////////////////////////////////////////////////////
template <typename T, T, typename P, P>
struct Proxy;
template <typename T, typename R, typename... Args, R (T::*mf)(Args...),
typename P, P p>
struct Proxy<R (T::*)(Args...), mf, P, p> {
static R call(Args... args) {
if (*p) {
return ((*(*p)).*mf)(args...);
} else {
return -1;
}
}
};
// Wrap the methods ///////////////////////////////////////////////////////////
#define WRAP(n, obj, m, ptr) \
const auto &n = Proxy<decltype(&obj::m), &obj::m, obj **, &ptr>::call
WRAP(wrap_a, Foo, a, FOO);
WRAP(wrap_b, Foo, b, FOO);
WRAP(wrap_c, Foo, c, FOO);
int main() {
// Test that it works
ptr_a = &wrap_a;
ptr_b = &wrap_b;
ptr_c = &wrap_c;
return ptr_b(0);
}
I have the following scenario:
struct AP;
struct B
{
B() : m(2) {}
int m;
};
struct A : private B
{
A() : B(), n(1) {}
private:
int n;
friend AP;
};
struct AP
{
AP(A& a) : a_(a) {}
template<typename T>
struct A_B {
using type = typename std::enable_if< std::is_base_of< typename std::remove_reference<T>::type,
A >::value,
T >::type;
};
template<typename T>
operator typename A_B<T>::type()
{
return static_cast<T>(a_);
}
template<typename T>
typename A_B<T>::type get()
{
return static_cast<T>(a_);
}
int& n() { return a_.n; }
private:
A& a_;
};
int main()
{
A a;
AP ap(a);
ap.n() = 7;
const B& b = ap.get<const B&>();
//const B& b = ap; candidate template ignored: couldn't infer template argument 'T'
//auto b = static_cast<const B&>(ap); candidate template ignored: couldn't infer template argument 'T'
std::cout<<b.m;
}
The commented lines wouldn't compile. Clang++ notes that "candidate template ignored: couldn't infer template argument 'T'"
Why am I not able to get a reference to A's base with the cast operator?
I think the code would look much nicer that way.
The answer that you posted works, but is overkill unless you really want a static_assert message.
Classic templating works just fine in this instance because A is already convertible to B:
struct AP
{
AP(A& a) : a_(a) {}
template<typename T>
operator T()
{
return a_;
}
template<typename T>
T get()
{
return a_;
}
int& n() { return a_.n; }
private:
A& a_;
};
Demo
I found the answer here: http://www.mersenneforum.org/showthread.php?t=18076
This is the key: "when you want the compiler to deduce argument types, those types must not be dependent types"
With this it compiles:
template<typename T>
operator T()
{
static_assert(std::is_base_of< typename std::remove_reference<T>::type,A >::value,
"You may cast AP only to A's base classes.");
return static_cast<T>(a_);
}
Hello I'm trying to use Boost.Test for my unit tests. However some of my functions return a collection of tuples, in a std::vector< std::tuple< TypeA, TypeB > >.
To work with BOOST_CHECK_EQUALS and BOOST_CHECK_EQUAL_COLLECTION, I specialized boost::test_tools::print_log_value to print nice tuples and vectors as the answer to this question explains. I also provide operator<< for tuples, so that my vector can use that while printing the whole vector. For cleanness this operator lies in the blank namespace.
However the compilation fails, as the implementation of boost::test_tools::print_log_value <std::vector<std::tuple<...>>> cannot find the operator<< for tuples.
Here is a minimal code which is, sorry, already quite verbose.
#define BOOST_TEST_MODULE my_test
#include <boost/test/included/unit_test.hpp>
#include <tuple>
#include <vector>
/////////////////
// std::vector //
/////////////////
// boost printing method
namespace boost {
namespace test_tools {
template< typename Type >
struct print_log_value< std::vector< Type > > {
void operator()( std::ostream& s, const std::vector< Type > &collection ) {
const int size = collection.size();
if( size == 0 ) {
s << "[]";
}
else {
s << "[ ";
for( int i =0; i <= size-2; ++i ) {
s << collection[i] << ", ";
}
s << collection[size-1] << " ]";
}
return s;
}
};
} //namespace test_tools
} //namespace boost
////////////////
// std::tuple //
////////////////
// recursive calls for printing
namespace tuple_print_aux{
template< int I, int J, typename... Types >
struct recursive_printer {
static void print( std::ostream& s, const std::tuple<Types...> &collection ) {
s << std::get< I >( collection ) << ", ";
recursive_printer< I+1, J-1, Types... >::print( s, collection );
}
};
template< int I, typename... Types >
struct recursive_printer< I, 1, Types... > {
static void print( std::ostream& s, const std::tuple<Types...> &collection ) {
s << std::get< I >( collection );
}
};
template< typename... Types >
void recursive_print( std::ostream& s, const std::tuple<Types...> &collection ) {
recursive_printer< 0, sizeof...(Types), Types... >::print( s, collection );
}
}
// output stream operator
template< typename... Types >
std::ostream& operator<<( std::ostream& s, const std::tuple<Types...> &collection ) {
s << "( ";
tuple_print_aux::recursive_print< Types... >( s, collection );
s << " )";
return s;
}
// boost printing method
namespace boost {
namespace test_tools {
template< typename... Types >
struct print_log_value< std::tuple< Types... > > {
void operator()( std::ostream& s, const std::tuple<Types...> &collection ) {
s << "( ";
tuple_print_aux::recursive_print< Types... >( s, collection );
s << " )";
}
};
} //namespace test_tools
} //namespace boost
BOOST_AUTO_TEST_CASE(my_test_case) {
//builds successfully
BOOST_CHECK_EQUAL( std::make_tuple(1,"a"), std::make_tuple(1,"a") );
//builds successfully
std::vector< int > v( 2, 3 ), w( 2, 7 );
BOOST_CHECK_EQUAL_COLLECTIONS( v.begin(), v.end(), w.begin(), w.end() );
//fails to build
std::vector< std::tuple<int,int> > a( 1, std::make_tuple(1,3) ), b( 1, std::make_tuple(2,2) );
BOOST_CHECK_EQUAL_COLLECTIONS( a.begin(), a.end(), b.begin(), b.end() );
};
Of course, putting the operator<< for std::tuples into the std namespace solve the problem, but this is a non-standard non-elegant solution.
So... how should I approach the problem. ?
Thank you for any help.
I believe you should declare
template< typename... Types >
std::ostream& operator<<( std::ostream& s, const std::tuple<Types...> &collection )
before the struct print_log_value< std::vector< Type > >. Also I think that the specialization of struct print_log_value for tuples is enough as the BOOST_CHECK_EQUAL_COLLECTIONS do not actually see the std::vector but an iterator on it.
Consider the following code:
#include <boost/iterator/iterator_facade.hpp>
#include <map>
// Class implements an stl compliant iterator to access the "sections" stored within a configuration.
template < typename _Iterator, typename _Reference >
class Section
: public boost::iterator_facade<
Section< _Iterator, _Reference >,
_Iterator,
boost::random_access_traversal_tag,
_Reference
>
{
private:
// Define the type of the base class:
typedef boost::iterator_facade<
Section< _Iterator, _Reference >,
_Iterator,
boost::random_access_traversal_tag,
_Reference
> base_type;
public:
// The following type definitions are common public typedefs:
typedef Section< _Iterator, _Reference > this_type;
typedef typename base_type::difference_type difference_type;
typedef typename base_type::reference reference;
typedef _Iterator iterator_type;
public:
explicit Section( const iterator_type it )
: m_it( it )
{ }
// Copy constructor required to construct a const_iterator from an iterator:
template < typename _U >
Section( const Section< _U, _Reference > it )
: m_it( it.m_it )
{ }
private:
// The following classes are friend of this class to ensure access onto the private member:
friend class boost::iterator_core_access;
template < typename _Iterator, typename _Reference > friend class Section;
void increment( ){ ++m_it; } // Advance by one position.
void decrement( ){ --m_it; } // Retreat by one position.
void advance( const difference_type& n ){ m_it += n }; // Advance by n positions.
bool equal( const this_type& rhs ) const{ return m_it == rhs.m_it; } // Compare for equality with rhs.
reference dereference( ) const { return m_it->second; } // Access the value referred to.
difference_type distance_to( const this_type& rhs ) const{ return rhs.m_it - m_it; } // Measure the distance to rhs.
private:
// Current "section" iterator:
iterator_type m_it;
};
struct Data
{
void f( ) const
{ }
};
typedef std::map< int, Data > map_type;
typedef Section< const map_type::const_iterator, const Data& > iterator_type;
map_type g_map;
iterator_type begin( )
{
return iterator_type( g_map.begin( ) );
}
void main( )
{
iterator_type i = begin( );
// i->f( ); // <--- error C2039: 'f' : is not a member of 'std::_Tree_const_iterator<_Mytree>'
( *i ).f( );
}
So the iterator facade shall return a reference to Data type. This works well when dereference operator is called but compile fails when operator->() is called. So I am a bit confused because operator->() tries to return a std::map::iterator. Any ideas ?
The iterator returns an iterator on dereference. To get the f part, you need to dereference twice.
It looks a lot like you misunderstood the meaning of the template arguments to iterator_facade. The second argument is not supposed to be any iterator type (this is what causes all your trouble). Instead you should use it to name your value_type.¹
From the way you specified the dereference operation (and Ref) and wanted to use it in main (i->f()) it looks like you just wanted to iterate the map's values. So, I'd rewrite the whole thing using more descriptive names as well, and here it is, working:
Live On Coliru
#include <boost/iterator/iterator_facade.hpp>
#include <map>
// Class implements an stl compliant iterator to access the "sections" stored within a configuration.
template <typename Map, typename Value = typename Map::mapped_type>
class MapValueIterator : public boost::iterator_facade<MapValueIterator<Map>, Value, boost::random_access_traversal_tag, Value const&> {
private:
// Define the type of the base class:
typedef Value const& Ref;
typedef boost::iterator_facade<MapValueIterator<Map>, Value, boost::random_access_traversal_tag, Ref> base_type;
public:
// The following type definitions are common public typedefs:
typedef MapValueIterator<Map> this_type;
typedef typename base_type::difference_type difference_type;
typedef typename base_type::reference reference;
typedef typename Map::const_iterator iterator_type;
public:
explicit MapValueIterator(const iterator_type it) : m_it(it) {}
// Copy constructor required to construct a const_iterator from an iterator:
template <typename U, typename V> MapValueIterator(const MapValueIterator<U,V> it) : m_it(it.m_it) {}
private:
// The following classes are friend of this class to ensure access onto the private member:
friend class boost::iterator_core_access;
template <typename U, typename V> friend class MapValueIterator;
void increment() { std::advance(m_it); } // Advance by one position.
void decrement() { std::advance(m_it, -1); } // Retreat by one position.
void advance(const difference_type &n) { std::advance(m_it, n); } // Advance by n positions.
bool equal(const this_type &rhs) const { return m_it == rhs.m_it; } // Compare for equality with rhs.
reference dereference() const { return m_it->second; } // Access the value referred to.
difference_type distance_to(const this_type &rhs) const { return rhs.m_it - m_it; } // Measure the distance to rhs.
private:
// Current iterator:
iterator_type m_it;
};
#include <iostream>
struct Data {
void f() const {
std::cout << __PRETTY_FUNCTION__ << "\n";
}
};
typedef std::map<int, Data> map_type;
template <typename Map>
MapValueIterator<Map> map_value_iterator(Map const& m) {
return MapValueIterator<Map>(m.begin());
}
int main() {
map_type g_map;
auto i = map_value_iterator(g_map);
i->f();
}
Which prints the output
void Data::f() const
as you'd expect.
Note that there are numerous places where I implemented the member functions using standard library facilities. Note as well, the iterator "mimics" random access, but it won't have the expected performance characteristics (increment is O(n)).
Final note: I'd recommend against having the implicit conversion constructor. I think you can do without it.
¹ The reference-type should typically be the same (but ref-qualified) except in rare cases where you actually "proxy" the values. This is an advanced topic and rarely should be used.
I would like to have compile-time selection of function pointers.
Something like functionListAutomatic 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 do know how to make funk_exist so that it evaluates 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 causes problem. The compiler tries to find a funk( int, string ) function and fails before it evaluates funk_exist(). I need an expression that evaluates funk_exist() and then does not evaluate funk( int, string ) if funk_exist() evaluates to false.
Appreciate your help.
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...>;
boilerplate injected!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 funk.
but who will funk? The 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 get 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 funk can be called in can_funk, but you can replace that with whatever trait you want, so long as it generates a compile-time bool.
In my case, the lambda acts as an adapter, so if the signatures don't match it will still generate a function pointer.
1 This just gives me a trait to detect if I can call funk on some arguments. You have your own, so you don't have to use it.
I would like to thank Yakk for his answer. With a few modifications I got that to work, and I have posted the complete program below.
However there were a few snafus.
The use of class template for Z in details pushes the evaluation out of details and out to funk_r. This means that the compiler tries to evaluate a funk( int, string ) which then gives a compile error. Apparently it is no possible to use SFINAE when making aliases with using so I did not find a solution for that.
#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. It is not possible to use class template here because then the SFINAE evaluation gets pushed out to funk_r<...>.