Variadic Macros issue

Hey guys,
I'm writing a wrapper for std::function which contains the name of std::function as string (for debugging purposes)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
template<typename ... Args>
class CExtended_Function
{
public:
CExtended_Function(const std::function<void(Args...)>& func_type, const std::string& func_name) :
				func(func_type),
				function_name(func_name)
{

}


const std::function<void(Args...)> func;


const std::string function_name;


};


1) first approach
I created a make function for this type

1
2
3
4
5
6
7
8
template<typename Func>
auto Make_Extended_Function_Impl(Func f, const std::string& name)
{
    std::function func(f);
    CExtended_Function res(f, name); //< error cannot deduce template arguments

    return res;
}

in combination with this macro
 
#define MAKE_EXTENDED_FUNCTION(f) Make_Extended_Function_Impl(f, #f) 

however this fails because template arguments were failed to deduce





2) approach

Next try was to create a varidic macro

1
2
3
4
5
6
7
8
9
10
template<typename Func, typename ... Args>
CExtended_Function<Args...> Make_Extended_Function_Impl2<Args...>(Func f, const std::string& name)
{
    std::function func(f);
    CExtended_Function<Args...> res(f, name);

    return res;
}

#define MAKE_EXTENDED_FUNCTION2(f, ...) Make_Extended_Function_Impl2<__VA_ARGS__>(f, #f) 



However if i use this with :

1
2
3
4
5
6
7
8
static void Test_Func();
static void Test_Func_float(float foo);

void foo()
 {
auto res = MAKE_EXTENDED_FUNCTION2(Test_Func);
auto res1 = MAKE_EXTENDED_FUNCTION2(Test_Func_float,float);
}


res evaluates to Make_Extended_Function_Impl2<>(Test_Func, "Test_Func") which produces a compiler error because no template arguments where passed to <>

res1 produces a compiler error:
Make_Extended_Function_Impl2 could not be resolved

So how would you solve this problem. I'm happy with either way 1(auto template argument deduction), or 2 (macros) Could you help me figure that out?


Last edited on
I think the normal function needs to use the function name macro (__func__ or ??) and pass it back in string format. I don't think you can get it at the higher level.
Something along these lines, perhaps
(needs to be refined; works with g++ and msvc++, but clang++/libc++ chokes on this):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include <iostream>
#include <functional>
#include <typeinfo>
#include <string>
#include <type_traits>
#include <cstring>

#ifdef __GNUG__ // GNU or compatible

  #include <cxxabi.h>
  #include <cstdlib>
  #include <memory>

  std::string demangle( const std::string& raw_name )
  {
      int result ;
      std::unique_ptr< char, decltype(&std::free) > ptr(
         __cxxabiv1::__cxa_demangle( raw_name.c_str(), nullptr, nullptr, std::addressof(result) ),
         &std::free ) ;
      return result == 0 ? ptr.get() : raw_name ;
  }

#else

  std::string demangle( const std::string& name ) { return name ; }

#endif // _GNUG_

template < typename FN >
struct function_wrapper : std::function<FN>
{
    using base = std::function<FN> ;
    using base::operator() ;
    using typename base::result_type ;
    using base::operator bool ;
    // etc.

    template < typename CALLABLE > function_wrapper( CALLABLE fn ) : base(fn) {}
    template < typename CALLABLE > function_wrapper( CALLABLE fn, const char* n ) : base(fn), name_(n) {}

    std::string target_name() const { return name_ ; }
    std::string type() const { return demangle( type_ ) ; }

    std::string name_  = "" ;
    std::string type_  = base::target_type().name();
};

// helper to remove the noexcept specifier from the type of a non-member function
template < typename T > struct remove_noexcept { using type = T ; } ;

template < typename R, typename... A > struct remove_noexcept< R(A...) noexcept >
{ using type = R( A... ) ; } ;

template < typename T > using remove_noexcept_t = typename remove_noexcept<T>::type;

// macro for non-member functions and pointers to non-member functions (type is deduced)
#define NAMED_FN(x) \
   function_wrapper< remove_noexcept_t< std::remove_pointer_t< decltype(x) > > >( x, #x )

// macro for all other callable objects (type needs to be specified)
#define NAMED_FN_WITH_TYPE(t,x) function_wrapper<t>( x, #x )

int main()
{
    auto fn = NAMED_FN(std::strcmp) ;
    std::cout << "name: " << fn.target_name()
              << "\ntype: " << fn.type() << '\n'
              << fn( "abcde", "abcd" ) << '\n' ;

    fn = NAMED_FN( &std::strcmp) ; // test remove_noexcept_t
    std::cout << "name: " << fn.target_name()
              << "\ntype: " << fn.type() << '\n'
              << fn( "abcde", "abcd" ) << "\n\n" ;

    const auto fn2 = NAMED_FN_WITH_TYPE( double(), std::bind( &std::istream::good, std::addressof(std::cin) ) ) ;
    std::cout << "name: " << fn2.target_name()
              << "\ntype: " << fn2.type() << '\n'
              << std::fixed << fn2() << "\n\n" ;

    const auto fn3 = NAMED_FN_WITH_TYPE( int( int, int ), std::plus<>() ) ;
    std::cout << "name: " << fn3.target_name()
              << "\ntype: " << fn3.type() << '\n' ;
    std::cout << fn3( 123, 456 ) << "\n\n" ;

    const auto fn4 = NAMED_FN_WITH_TYPE( void( const char* ), [] ( auto v ) { std::cout << "hello " << v << "!\n" ; } ) ;
    std::cout << "name: " << fn4.target_name()
              << "\ntype: " << fn4.type() << '\n' ;
    fn4( "world" ) ;
}

http://coliru.stacked-crooked.com/a/44eee8b447fc9976
https://rextester.com/ZFG36451
Looks like that version of libc++'s std::is_function is wrong.

For example, attempting to instantiate std::is_function<bool() const> yields a hard error substitution failure.

(Maybe that's obvious, I don't know. It took me a while to figure out.)
Last edited on
hey thank you all for your fast replies :)
I decided to go with JLBorges solution :)

however i got another strange issue


this one works fine

1
2
3
4
5
6
7
//with Callback
void Callback();

void foo()
{
auto func3 = Make_Extended_Function_Impl(Callback,"void_test");
}




however this one

1
2
3
4
5
6
Callback_float(float test_float);

void foo()
{
auto func3 = Make_Extended_Function_Impl(Callback,"void_test");
}


does not.

The compiler complains about the conversion to std::function here

1
2
3
4
5
6
7
8
template<typename Func, typename ... Args>
CExtended_Function<Args...> Make_Extended_Function_Impl(const Func& f, const std::string& name)
{
	std::function<void(Args...)> func(f);   ///< this line causes the error
	CExtended_Function<Args...> res(func, name);

	return res;
}



1
2
3
 In instantiation of CExtended_Function<Args ...> Make_Extended_Function_Impl(const Func&, const string&) [with Func = void(float); Args = {}; std::__cxx11::string = std::__cxx11::basic_string<char>]':
../Src/User/Testcases/Test_Events.cpp:50:71:   required from here
/Inc/User/events/CEventHandler.hpp:330:31: error: no matching function for call to 'std::function<void()>::function(void (&)(float))' 


Why does f have a void(void) signature instead of void(float) ? Do you have an idea?
Last edited on
The problem is that the compiler cannot deduce Args.

So when you want to use std::function (with void return), why don't you say it in your parameter:
1
2
3
4
5
6
7
template<typename ... Args>
CExtended_Function<Args...> Make_Extended_Function_Impl(const std::function<void(Args...)>& func, const std::string& name)
{
	CExtended_Function<Args...> res(func, name);

	return res;
}
if i do it like this it does not work either
1
2
auto func3 = Make_Extended_Function_Impl(Callback,"void_test");
auto func4 = Make_Extended_Function_Impl(Callback_float2,"float_test");


Both of these lines fail (I'm only posting the errors for the func3 call (the error is already long enough)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
../Src/User/Testcases/Test_Events.cpp:53:63: error: no matching function for call to 'Make_Extended_Function_Impl(void (&)(), const char [10])'
auto func3 = Make_Extended_Function_Impl(Callback,"void_test");

Inc/User/events/CEventHandler.hpp:332:29: note: candidate: template<class ... Args> CExtended_Function<Args ...> NEvents::Make_Extended_Function_Impl(const std::function<void(Args ...)>&, const string&)
 CExtended_Function<Args...> Make_Extended_Function_Impl(const std::function<void(Args...)>& func, const std::string& name)
                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~
/Inc/User/events/CEventHandler.hpp:332:29: note:   template argument deduction/substitution failed:
../Src/User/Testcases/Test_Events.cpp:53:63: note:   mismatched types 'const std::function<void(Args ...)>' and 'void()'
  auto func3 = Make_Extended_Function_Impl(Callback,"void_test");




Inc/User/events/CEventHandler.hpp:339:29: note: candidate: template<class ... Args> NEvents::CExtended_Function<Args ...> NEvents::Make_Extended_Function_Impl(const std::function<void(Args ...)>&, const char*)
 CExtended_Function<Args...> Make_Extended_Function_Impl(const std::function<void(Args...)>& func, const char* pName)
                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~
/Inc/User/events/CEventHandler.hpp:339:29: note:   template argument deduction/substitution failed:
../Src/User/Testcases/Test_Events.cpp:53:63: note:   mismatched types 'const std::function<void(Args ...)>' and 'void()'
  auto func3 = Make_Extended_Function_Impl(Callback,"void_test");



I don't get what template argument deduction fails here?

As you can see, i provided an overload for the make function. A string version and a char* Version to rule out that as a problem.

1
2
3
4
5
template<typename ... Args>
CExtended_Function<Args...> Make_Extended_Function_Impl(const std::function<void(Args...)>& func, const std::string& name);

template<typename ... Args>
CExtended_Function<Args...> Make_Extended_Function_Impl(const std::function<void(Args...)>& func, const char* pName);



The compiler already sees the void_test function as void(void).
Make_Extended_Function_Impl(void (&)(), const char [10])

Is the & in void (&)() correct? Or does this indicate an error?

What do you think?





Last edited on
I would advise giving up on template argument deduction for your function wrapper.

Reconsider whether or not you need the polymorphic behavior of std::function.
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include <iostream>
#include <functional>
#include <typeinfo>
#include <string>
#include <type_traits>
#include <cstring>

#ifdef __GNUG__ // GNU or compatible

  #include <cxxabi.h>
  #include <cstdlib>
  #include <memory>

  std::string demangle( const std::string& raw_name )
  {
      int result ;
      std::unique_ptr< char, decltype(&std::free) > ptr(
         __cxxabiv1::__cxa_demangle( raw_name.c_str(), nullptr, nullptr, std::addressof(result) ),
         &std::free ) ;
      return result == 0 ? ptr.get() : raw_name ;
  }

#else

  std::string demangle( const std::string& name ) { return name ; }

#endif // _GNUG_

template < typename FN >
struct extended_function : std::function<FN>
{
    using base = std::function<FN> ;
    using base::operator() ;
    using typename base::result_type ;
    using base::operator bool ;
    // etc.

    template < typename CALLABLE > extended_function( CALLABLE fn ) : base(fn) {}
    template < typename CALLABLE > extended_function( CALLABLE fn, const std::string& n ) : base(fn), name_(n) {}

    std::string target_name() const { return name_ ; }
    std::string target_type() const { return demangle( base::target_type().name() ) ; }
    std::string wrapper_type() const
    { return "extended_function< " + demangle( typeid(FN).name() ) + " >" ; }

    std::string name_  = "" ;
};

// helper to remove the noexcept specifier from the type of a non-member function
template < typename T > struct remove_noexcept { using type = T ; } ;

template < typename R, typename... A > struct remove_noexcept< R(A...) noexcept >
{ using type = R( A... ) ; } ;

template < typename T > using remove_noexcept_t = typename remove_noexcept<T>::type;

template< typename FN >
auto make_extended_function( const FN& fn, const std::string& name )
{
    using func_type = std::remove_pointer_t< remove_noexcept_t<FN> > ;
    static_assert( std::is_function<func_type>::value ) ;
    return extended_function<func_type>( fn, name ) ;
}

void test() { std::cout << "function void test()\n" ; }

auto plus( int a, double b, long long c ) noexcept { return a+b+c ; }

// macro for non-member functions and pointers to non-member functions (uses the default name)
#define NAMED_FN(x) make_extended_function( x, #x )

int main()
{
    const auto t = make_extended_function( test, "this is the non-member function test" ) ;
    std::cout << "name: " << t.target_name()
              << "\nwrapper type: " << t.wrapper_type()
              << "\ntarget type: " << t.target_type() << '\n' ;
    t() ;

    const auto t1 = NAMED_FN(test) ; // use macro (name is the name of he function)
    std::cout << "\nname: " << t1.target_name()
              << "\nwrapper type: " << t1.wrapper_type()
              << "\ntarget type: " << t1.target_type() << '\n' ;
    t1() ;


    const auto p = make_extended_function( plus, "a custom name for plus" ) ;
    std::cout << "\n\nname: " << p.target_name()
              << "\nwrapper type: " << p.wrapper_type()
              << "\ntarget type: " << p.target_type() << '\n'
              << "p(1,2.3,4) returned " << p(1,2.3,4) << '\n' ;

    const auto p1 = NAMED_FN(plus) ; // use macro (name is the name of he function)
    std::cout << "\nname: " << p1.target_name()
              << "\nwrapper type: " << p1.wrapper_type()
              << "\ntarget type: " << p1.target_type() << '\n'
              << "p1(1,2.3,4) returned " << p1(1,2.3,4) << '\n' ;
}

http://coliru.stacked-crooked.com/a/1e9353778fe3734a
https://rextester.com/QHIXAO63698
wow thx this solution works really nice. :)
Would you mind walking me a little bit through?

this first part is pretty clear to me.

However here is where the magic starts :)
what's the purpose of these using directives?

1
2
3
4
5
using base = std::function<FN>;
	using base::operator();
	using typename base::result_type;
	using base::operator bool;
	// etc. 


why did you add an extra template paramter to the constructor?
1
2
3
4
5
6
7
8
9
10
11
template<typename CALLABLE> 
	extended_function(CALLABLE fn) :
					base(fn)
	{
	}
	template<typename CALLABLE>
	extended_function(CALLABLE fn, const std::string& n) :
					base(fn),
					name_(n)
	{
	}


why not use FN from the struct declaration?


why do we need to remove the noxecept specifier?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// helper to remove the noexcept specifier from the type of a non-member function
template<typename T> struct remove_noexcept
{
	using type = T;
};

why do we need to remove Ts pointer? Is it that the function name does not contain a (*)?

[code]template<typename FN>
auto make_extended_function(const FN& fn, const std::string& name)
{
	using func_type = std::remove_pointer_t< remove_noexcept_t<FN> >;
	static_assert( std::is_function<func_type>::value ,"not a valid function");
	return extended_function<func_type>(fn, name);
}



and last but not least why did you choose to make extended_function a struct instead of a class?



template<typename R, typename ... A> struct remove_noexcept<R(A...) noexcept >{ using type = R( A... );};

template<typename T> using remove_noexcept_t = typename remove_noexcept<T>::type;[/code]

> what's the purpose of these using directives?

They are not required for this example (in the constructor we could write the long name for the base class).


> why did you add an extra template parameter to the constructor?
> why not use FN from the struct declaration?

We want our wrapper to be polymorphic, a la std::function<>. Our wrapper should be able to wrap any callable object (for example, the result of a bind expression) as long as its arguments and result are compatible with those of the wrapper.


> why do we need to remove the noxecept specifier?

There is no partial specialisation of std::function<> that accepts a noexcept specifier.
specifying a callable type with a noexcept specifier would give us an error:
http://coliru.stacked-crooked.com/a/d93d5e52af66591b


> and last but not least why did you choose to make extended_function a struct instead of a class?

It is a class; a class that uses the class key struct.
The class keys struct and class are indistinguishable in C++, except that the default access mode and default inheritance mode are public if class declaration uses the struct class-key and private if the class declaration uses the class class-key. Both class and struct can be used in a class definition.
https://en.cppreference.com/w/cpp/language/classes

Hey JLBorges:
thx for your very clear answer. Your were a big help to me :)
Topic archived. No new replies allowed.