Implicit template type deduction

Thanks for taking a look at my thread,

Not sure if the title is accurate - I don't really know what to look for online to answer my question.

Let's say I have a class like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdexcept>
#include <functional>

class Throwable {
public:

	Throwable() {

	}

	template<typename type_signature>
	static typename std::function<type_signature>::result_type invoke(std::function<type_signature> function) {
		typename std::function<type_signature>::result_type result;
		bool success = (result = function() != 0);
		if (!success) {
			throw std::runtime_error("failure");
		}
		return result;
	}

private:

};

To be used in a way similar to this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>

bool return_false() {
	return false;
}

int main() {

	int exit = EXIT_SUCCESS;

	try {

		auto result = Throwable::invoke<bool()>(return_false);

	}
	catch (const std::runtime_error& exception) {
		exit = EXIT_FAILURE;
		std::cerr << "Exception occurred: " << exception.what() << "!" << std::endl;
	}

	return exit;
}


On line 13 of the main function, I'd like to be able to have the template type deduced implicitly, so that I may write auto result = Throwable::invoke(return_false);. I intend to use this for many of the WINAPI functions, the return types of which are all over the place. They usually return an int or a typedef of an int, or void pointer (ATOM, INT, BOOL etc) or something else entirely. Incidentally, the next challenge would be to allow for an arbitrary number of arguments of different types, so the signature of the std::function object really has to be flexible.
I've been looking at this code too long, am feeling stuck. Perhaps this is a weird idea to begin with. Any input is appreciated.
Last edited on
C++ 17 has std::invoke<>() http://en.cppreference.com/w/cpp/utility/functional/invoke
The current versions of the GNU and microsoft libraries have implementatoin of std::invoke<>()
For older versions, see 'possible implementation' on the cppreference page

Something like this, perhaps:
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
#include <stdexcept>
#include <functional>
#include <type_traits>
#include <iostream>
#include <string>

namespace throwable { // favour a namespace over a class for this

    // http://en.cppreference.com/w/cpp/types/enable_if
    // http://en.cppreference.com/w/cpp/types/is_convertible
    template < typename T >
    inline typename std::enable_if< std::is_convertible<T,bool>::value >::type
    throw_if_false( const T& v ) { if( !bool(v) ) throw std::runtime_error( "failure" ) ; }

    template < typename T >
    inline typename std::enable_if< !std::is_convertible<T,bool>::value >::type
    throw_if_false( const T& ) {} // not convertible to bool: do nothing

    template < typename FN, typename... ARGS >
    inline auto invoke( FN&& fn, ARGS&&... args ) {

       // http://en.cppreference.com/w/cpp/utility/functional/invoke (C++17)
       // http://en.cppreference.com/w/cpp/utility/forward
       auto result = std::invoke( std::forward<FN>(fn), std::forward<ARGS>(args)... ) ;
       throw_if_false(result) ;
       return result ;
    }
}

int main() {

     try {

         auto result1 = throwable::invoke( [] ( auto a, auto b ) { return a+b ; }, 32, 7 ) ;
         std::cout << "ok: result1 == " << result1 << '\n' ;

         auto result2 = throwable::invoke( [] ( auto a, auto b ) { return a[b] ; }, "abcd", 2 ) ;
         std::cout << "ok: result2 == '" << result2 << "'\n" ;

         auto result3 = throwable::invoke( [] ( auto a ) -> std::string { return a ; }, "abcd" ) ;
         std::cout << "ok: result3 == '" << result3 << "'\n" ;

         auto result4 = throwable::invoke( [] ( auto a, auto b ) { return a - b*2 ; }, 18, 9 ) ;
         std::cout << "ok: result4 == '" << result4 << "'\n" ;
     }

     catch( const std::exception& e ) {

         std::cerr << "error: " << e.what() << '\n' ;
         return 1 ;
     }
}

http://coliru.stacked-crooked.com/a/e34c1706733cf817
OP: you were so close, just needed to catch const std::exception&:
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
#include <iostream>
#include <stdexcept>
#include <functional>

class Throwable {
public:

	Throwable() {}

	template<typename type_signature>
	static typename std::function<type_signature>::result_type invoke(std::function<type_signature> function)
	{
		typename std::function<type_signature>::result_type result;
		bool success = (result = function() != 0);
		if (!success)
        {
			throw std::runtime_error("failure");
		}
		return result;
	}
};
bool return_false() {return false;}

int main()
{
    try
    {
        auto result = Throwable::invoke<bool()>(return_false);
	}
	catch (const std::exception& e) {

		std::cerr << e.what() << "!" << "\n";
	}
}

and, for what it's worth, while tinkering with your program i came up with this alternate version: [code]
http://coliru.stacked-crooked.com/a/c0e5b67bdc8bc775
> OP: you were so close, just needed to catch const std::exception&:

catch (const std::runtime_error& exception) in the original code is perfectly fine.
http://coliru.stacked-crooked.com/a/39788edfe1d1333a


What is required was precisely stated in the original post:

a. I'd like to be able to have the template type deduced implicitly, so that I may write
auto result = Throwable::invoke(return_false);

b. allow for an arbitrary number of arguments of different types


Enhanced version (allow using throwable::invoke with callable objects with void as the result type):

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
#include <stdexcept>
#include <functional>
#include <type_traits>

namespace throwable {

    namespace {

        // http://en.cppreference.com/w/cpp/types/enable_if
        // http://en.cppreference.com/w/cpp/types/is_convertible
        template < typename T >
        inline typename std::enable_if< std::is_convertible<T,bool>::value >::type
        throw_if_false( const T& v ) { if( !bool(v) ) throw std::runtime_error( "failure" ) ; }

        template < typename T >
        inline typename std::enable_if< !std::is_convertible<T,bool>::value >::type
        throw_if_false( const T& ) {} // not convertible to bool: do nothing

        template < typename DECAYED_RESULT_TYPE, typename FN, typename... ARGS >
        struct do_invoke {

            static auto invoke( FN&& fn, ARGS&&... args ) {

               auto&& result = std::invoke( std::forward<FN>(fn), std::forward<ARGS>(args)... ) ;
               throw_if_false(result) ;
               return result ;
            }
        };

        template < typename FN, typename... ARGS >
        struct do_invoke< void, FN, ARGS... > {

            static auto invoke( FN&& fn, ARGS&&... args ) {

               std::invoke( std::forward<FN>(fn), std::forward<ARGS>(args)... ) ;
            }
        };
    }

    template < typename FN, typename... ARGS >
    inline auto invoke( FN&& fn, ARGS&&... args ) {

       using result_type = decltype( std::invoke( std::forward<FN>(fn), std::forward<ARGS>(args)... ) ) ;
       using do_invoke_type = do_invoke< result_type, FN, ARGS... > ;

       return do_invoke_type::invoke( std::forward<FN>(fn), std::forward<ARGS>(args)... ) ;
    }
}

http://coliru.stacked-crooked.com/a/264c6ce9d088296c

TO DO: handle callable objects that return lvalues or xvalues.

What is required was precisely stated in the original post:

a. I'd like to be able to have the template type deduced implicitly, so that I may write
auto result = Throwable::invoke(return_false);


indeed, you're quite right. in that case similar to these ...
1
2
3
 throwable::invoke( [] ( auto& a, auto b, auto c ) -> void { a[b] = c ; }, str, 5, '*' ) ;
//...
 auto result3 = throwable::invoke( [] () -> A { return {} ; } ) ;

the return type of throwable::invoke() has to be stated explicity since it can't be evaluated directly from the auto declarations as in the other examples. so something like this would be on similar lines:
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
#include <iostream>
#include <stdexcept>
#include <functional>

bool return_false(void) {return false;}
std::function<bool()> myFunc = return_false;

class Throwable {
public:

	Throwable() {}

	template<typename type_signature>
	static type_signature invoke(std::function<type_signature(void)> myFunc)
	try
	{
        type_signature success = myFunc();
		if (!success)
        {
			throw std::runtime_error("failure");
		}
		return success;
	}
	catch (const std::exception& e)
	{
	    std::cerr << e.what();
        return 0;
	}
};

int main()
{
   auto result = Throwable::invoke<bool>(return_false);//prints "failure"
   // auto result = Throwable::invoke(myFunc);//alternate version
}


I need to go to sleep, but here's my start of an attempt:
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
# include <iostream>
template <typename F>
auto make_fail_if_false(F&& f) {
  return [function{std::forward<F>(f)}] (auto&&... args) {
    auto&& result = function(std::forward<decltype(args)>(args)...);
    if constexpr
      (std::is_convertible_v<decltype(result), bool>) 
        if(!result) throw std::runtime_error("failure");
    return result;
  };
}

// borrowing JLBorges' test cases:
int main() {
  try {
    auto result1 = make_fail_if_false([](auto a, auto b){ return a + b; })(32, 7);
    std::cout << "ok: result1 == " << result1 << '\n' ;

    auto result2 = make_fail_if_false([] (auto a, auto b) { return a[b] ; })("abcd", 2 );
    std::cout << "ok: result2 == '" << result2 << "'\n" ;

    auto result3 = make_fail_if_false( [] ( auto a ) -> std::string { return a; })("abcd") ;
    std::cout << "ok: result3 == '" << result3 << "'\n" ;

    auto result4 = make_fail_if_false([] ( auto a, auto b ) { return a - b*2 ; })(18, 9 ) ;
    std::cout << "ok: result4 == '" << result4 << "'\n" ;
  }

  catch( const std::exception& e ) {

    std::cerr << "error: " << e.what() << '\n' ;
    return 1 ;
  }
}

Compiles under G++7.0.1 (which is the upstream branch); it's close to working under 6.3.0 except for the constexpr if:
Does anyone know a live compiler that supports the experimental branch?
See it live, here:
http://melpon.org/wandbox/permlink/Wqm0BbKbvfpj8xE4
ok: result1 == 39
ok: result2 == 'c'
ok: result3 == 'abcd'
error: failure
Last edited on
> in that case similar to these ...
> ...
> the return type of throwable::invoke() has to be stated explicity since it can't be evaluated
> directly from the auto declarations as in the other examples.

No.

Forcing the user to explicitly state the precise return type is a terrible palooka kludge, prone to silent errors.
(Hint: std::function<> stores a type-erased callable object; think about it.)

In any case, forcing the user to specify the result type doesn't even address the problem that you attempted to solve.
This would still be an error: auto result3 = Throwable::invoke<void>( [] {} ) ;

A sane, safe mechanism to allow that particular construct (assuming that it is actually required, or even useful) is embarrassingly trivial.

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
namespace throwable {

    namespace {
        
        // ...

        template < typename FN, typename... ARGS >
        struct do_invoke< void, FN, ARGS... > {

            struct void_wrapper 
            { friend std::ostream& operator<< ( std::ostream& stm, void_wrapper ) { return stm << "'void'" ; } };

            static auto invoke( FN&& fn, ARGS&&... args ) {

                std::invoke( std::forward<FN>(fn), std::forward<ARGS>(args)... ) ;
                return void_wrapper{} ;
            }
        };
    }

    template < typename FN, typename... ARGS >
    inline auto invoke( FN&& fn, ARGS&&... args ) {

        using result_type = decltype( std::invoke( std::forward<FN>(fn), std::forward<ARGS>(args)... ) ) ;
        using do_invoke_type = do_invoke< result_type, FN, ARGS... > ;

        return do_invoke_type::invoke( std::forward<FN>(fn), std::forward<ARGS>(args)... ) ;
    }
}

struct A{ friend std::ostream& operator<< ( std::ostream& stm, A ) { return stm << "A{}" ; } };

int main() {

     try {
         
         // ...

         // return void
         std::string str = "abcdefghijkl" ;
         auto result3 = throwable::invoke( [] ( auto& a, auto b, auto c ) -> void { a[b] = c ; }, str, 5, '*' ) ;
         std::cout << "ok: result3 == " << result3 << ", str == " << str << '\n' ; 
         
         // ...
    
     }

     catch( const std::exception& e ) {

         std::cerr << "error: " << e.what() << '\n' ;
         return 1 ;
     }
}

http://coliru.stacked-crooked.com/a/a0647dfc2df5e676
> Does anyone know a live compiler that supports the experimental branch?

godbolt https://godbolt.org/g/e9qVZs but it only compiles (does not link and run a program)

I suppose you do realise that constructing a polymorphic call wrapper for each call is somewhat expensive.
with the trailing return type specified, as in the instances mentioned above, can't any variable can be declared with/as auto? so I'm not sure I can see what extra this approach adds:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <string>

struct A{std::string m_name; A(const std::string& name) : m_name(name){}};
std::ostream& operator << (std::ostream& os, const A& a)
    {os << a.m_name << "\n"; return os;}

int main()
{
    auto result = [](auto a) -> std::string{return a;}("abcd");
    std::cout << result << "\n";
    auto result1 = [](auto a) -> A {return a;}(A{"John"});
    std::cout << result1 << "\n";
}
Good morning!
I suppose you do realise that constructing a polymorphic call wrapper for each call is somewhat expensive.

Yes, somewhat -- the only overhead comes from the closure's constructor and forwarding f (all of the polymorphism in that snippet is static). Of course, the resultant closure can be reused, which helps minimize the overhead.

That approach is worth posting because it is both clearer and shorter than the zero-cost approach we're discussing otherwise.

Also, thanks for pointing me to Godbolt... it's a shame that it won't link&run.

with the trailing return type specified, as in the instances mentioned above, can't any variable can be declared with/as auto? so I'm not sure I can see what extra this approach adds
Throwable::invoke<bool()>(return_false);

OP's code requires that bool() is provided as the template argument. If it's mismatched, it will cause errors, and it must be present. This is because std::function is type-erased.
The goal of the above is to add generality while making it so that the type of a function call expression gets deduced.... and doing this for free.

The result type can be obtained with an expression like this:
decltype( std::invoke( std::forward<FN>(fn), std::forward<ARGS>(args)... ) ), or in my example by deducing the return type of a function which returns only values with the type of the expression
function(std::forward<decltype(args)>(args)...).

The example implementation below contains another example, (it's straightforward, if really noisy):
http://en.cppreference.com/w/cpp/utility/functional/invoke
Last edited on
Morning to you too Max and kindly bear with me one more time as I'm still failing to see if std::invoke<>() etc is absolutely necessary. For e.g could we still do 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
#include <iostream>
#include <stdexcept>
#include <functional>

bool return_false(void) {return false;}
std::function<bool()> myFunc = return_false;

class Throwable {
public:

	Throwable() {}

	template<typename type_signature>
	static type_signature invoke(std::function<type_signature(void)> myFunc)
	try
	{
        type_signature success = myFunc();
		if (!success)
        {
			throw std::runtime_error("failure");
		}
		return success;
	}
	catch (const std::exception& e)
	{
	    std::cerr << e.what();
        return 0;
	}
};

int main()
{
   auto result = Throwable::invoke(myFunc);//no (explicit) template argument required

   //auto result = [](auto a) -> bool{return a;}(Throwable::invoke(myFunc));//alternate version on lines of JLBorges, MaxB
}

of course there could be other reasons behind alternate approach(es) but whether desired outcome might be achievd with tools predating C++17 - that's what I'm trying to figure out though I fear we have taken up too much of OP's thread already, sorry xismn
You certainly don't need C++17.... the code's just uglier (e.g., constexpr-if goes back to SFINAE, and so on. My approach in particular gets much uglier.)

Mentally I phrased the problem as
"Given any callable, call it with some arguments and see if the result is false. If it's false, throw. Otherwise, return the result."

Your approach is essentially correct (you at least shouldn't catch the exception inside invoke), but another requirement is to accept any number of arguments of different types. You can do that too, I suppose:
1
2
3
4
5
6
7
8
9
10
11
12
template<typename type_signature, typename...Args>
static type_signature invoke(std::function<type_signature(Args...)> myFunc, Args&&... args)
try {
  type_signature success = myFunc(std::forward<Args>(args)...);
  if (!success) {
    throw std::runtime_error("failure");
  }
  return success;
} catch (const std::exception& e) {
  std::cerr << e.what();
  return 0;
}

But that's just a contrived version of std::invoke that also happens to check the return value. It's a version of std::invoke because the above basically calls some function with some arguments; everything gets deduced, hopefully, although unfortunately no implicit conversions between arguments are possible (see http://coliru.stacked-crooked.com/a/ac2e8c8ffddbef98 ).

JLBorges' solution is very similar to that, but more generic: std::function goes away for some generic callable (named FN) and stuff gets perfect-forwarded. Relaxing the signature on the callable means that conversions between arguments are possible (that's just duck typing to see if the arguments match the callable's signature.)

JLBorges' code also uses SFINAE to handle the case where the callable's result is not convertible to bool. I copied that feature too, but I no longer think it's a good idea to support that case.

My solution is essentially the same thing with (another level of) higher-order programming.
Last edited on
mbozzi - thank you very much indeed for taking the time to draft such a detailed, thoughtful reply. This was very informative and great material overall. Many thanks again
> Does anyone know a live compiler that supports the experimental branch?
godbolt https://godbolt.org/g/e9qVZs but it only compiles (does not link and run a program)


http://melpon.org/wandbox runs things, currently on
gcc HEAD 7.0.1 20170315 (experimental)
and
clang HEAD 5.0.0 (https://github.com/llvm-mirror/clang.git 3fde646dcb36abca51c9cd4b25e224338f1728cf) (https://github.com/llvm-mirror/llvm.git 924375d273ef90b399590498f58392590627b5df)
Last edited on
@Cubbi
Oh, thanks!

@gunnerfunner
You're always welcome. I usually learn quite a lot myself while trying to write these posts :).

@xismn
What you want can be expressed with a simple function composition.
result = throw-if-false(some-winapi-call(a1, a2, a3)) where throw-if-false is otherwise the identity;
Consider:

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
# include <iostream>
# include <stdexcept>

template <typename T> decltype(auto) throw_if_false(T&& arg) {
  if (! arg) throw std::runtime_error("failure: arg is false");
  return std::forward<T>(arg);
}

namespace throwable {
  template <typename Fn, typename... Args>
  decltype(auto) invoke(Fn&& fn, Args&&... args) {
    return throw_if_false(std::forward<Fn>(fn)
                          (std::forward<Args>(args)...));
  }
}

static int foo = 1;

// always fails (seems about right, in my experience)
void* some_winapi_call () noexcept { return nullptr; }
// returns lvalue reference
int & get_ref()           noexcept { return foo;     }

int main() {
  try {
    // always fails
    std::cout << "calling some_winapi_call():\n";
    throwable::invoke(some_winapi_call);
  } catch(std::runtime_error const& e) {
    std::cerr << e.what() << "\n\n";
  }

  try {
    std::cout << "calling get_ref()\nset get_ref() = 0\n";
    throwable::invoke(get_ref) = 0;
    std::cout << "calling get_ref()\n";
    throwable::invoke(get_ref);
  } catch (std::runtime_error const& e) {
    std::cerr << e.what() << "\n\n";
  }

  try {
    auto result1 = throwable::invoke([](auto a, auto b)
                                     { return a + b; }, 32, 7);
    std::cout << "ok: result1 == '" << result1 << "'\n";

    auto result2 = throwable::invoke([](auto a, auto b)
                                     { return a[b]; }, "abcd", 2);
    std::cout << "ok: result2 == '" << result2 << "'\n";

    // doesn't compile -- not convertible to bool
    // auto result3 = throwable::invoke([](auto a ) -> std::string
    //                                  { return a; }, "abcd");
    // std::cout << "ok: result3 == '" << result3 << "'\n";

    auto result4 = throwable::invoke([](auto a, auto b)
                                     { return a - b*2; }, 18, 9);
    std::cout << "ok: result4 == '" << result4 << "'\n";
  }
  catch (const std::exception& e) {
    std::cerr << e.what() << "\n";
  }
}

http://coliru.stacked-crooked.com/a/fa4d40d96c29f590
Last edited on
Thank you everyone for your valuable input! I was pleasantly surprised to find out that my thread suddenly became so popular! I certainly have my reading cut out for me.
Topic archived. No new replies allowed.