I could have two different functions for void and non void, but that is a kludgy solution !
O_o
You have created this situation for yourself That said, it is not really a kludgy solution because `void' variables isn't a thing; only semantically and syntactically valid statements must be used in the code writing the function you want to write which means two different bits of code at some level because you have two entirely different things you want to do.
A conforming compiler will allow you to "return" a "void statement" with a "return expression". You aren't doing that because you are declaring a variable with assignment and trying to return that which is a named "void variable" (which isn't even a thing).
The problem is the way compilers examine code. You can't circumvent that checking with `std::conditional' or `std::enable_if'. Those are simply tools that can be used to build what you want without simply duplicating the entire function.
You can explore this code for three different options.
The first is used everywhere within the C++ standard library. (It isn't specified in the standard. It is a thing that is required by implementations of the standard library.)
The second is used by a lot of "meta-programmers" because it isolates the mechanism behind the scenes where it can be used directly instead of partially duplicating the mechanics of the function as with the first method.
The third is used by a crazy huge number of "WIN32API" programmers where arbitrary cleanup must be done regardless of how control leaves a function which isn't a bad choice in the case where the lamda throws an exception.
Soma
Code:
#include <functional>
#include <iostream>
using namespace std;
/*****************************************************************/
/*****************************************************************/
/*****************************************************************/
struct MTrue{};
struct MFalse{};
template
<
typename FFunction
>
struct MIsVoid
{
typedef MFalse UResult;
};
template <> struct MIsVoid<void>
{
typedef MTrue UResult;
};
/*****************************************************************/
/*****************************************************************/
/*****************************************************************/
template
<
typename FFunction
, typename FResult
>
void TestImplementation
(
FFunction fFunction
, MTrue
)
{
// prefix
fFunction(); // This SHOULD be bound so to prevent duplication of forwarding.
// postfix
}
template
<
typename FFunction
, typename FResult
>
FResult TestImplementation
(
FFunction fFunction
, MFalse
)
{
// prefix
FResult sResult(fFunction()); // This SHOULD be bound so to prevent duplication of forwarding.
// postfix
return(sResult);
}
template
<
typename FFunction
>
typename result_of<FFunction()>::type TestInterface
(
FFunction fFunction
)
{
typedef typename result_of<FFunction()>::type result_type;
typedef typename MIsVoid<typename result_of<FFunction()>::type>::UResult IsVoid;
// You SHOULD bind all parameters for the sake of portability.
// You MUST do this in one line for the sake of portability.
return(TestImplementation<FFunction, result_type>(fFunction, (IsVoid())));
}
/*****************************************************************/
/*****************************************************************/
/*****************************************************************/
struct MVoid{};
template
<
typename FFunction
, typename FResult
>
void ConditionallyAssign
(
FResult & fResult
, FFunction fFunction
, MTrue
)
{
fFunction(); // This SHOULD be bound so to prevent duplication of forwarding.
}
template
<
typename FFunction
, typename FResult
>
void ConditionallyAssign
(
FResult & fResult
, FFunction fFunction
, MFalse
)
{
fResult = fFunction(); // This SHOULD be bound so to prevent duplication of forwarding.
}
template
<
typename FFunction
>
typename conditional<is_void<typename result_of<FFunction()>::type>::value, MVoid, typename result_of<FFunction()>::type>::type TestWrapped
(
FFunction fFunction
)
{
typedef typename result_of<FFunction()>::type result_type;
typedef typename MIsVoid<typename result_of<FFunction()>::type>::UResult IsVoid;
typename conditional<is_void<typename result_of<FFunction()>::type>::value, MVoid, typename result_of<FFunction()>::type>::type result;
// prefix
// You SHOULD bind all parameters for the sake of portability.
ConditionallyAssign(result, fFunction, (IsVoid()));
// postfix
return(result);
}
/*****************************************************************/
/*****************************************************************/
/*****************************************************************/
template
<
typename FCleanup
>
struct CleanupAtExit
{
CleanupAtExit
(
FCleanup fCleanup
):
mCleanup(fCleanup)
{
}
~CleanupAtExit()
{
mCleanup();
}
FCleanup mCleanup;
};
template
<
typename FCleanup
>
CleanupAtExit<FCleanup> CraftCleanup
(
FCleanup fCleanup
)
{
return(fCleanup);
}
template
<
typename FFunction
>
typename result_of<FFunction()>::type TestCleanup
(
FFunction fFunction
)
{
typedef typename result_of<FFunction()>::type result_type;
typedef typename MIsVoid<typename result_of<FFunction()>::type>::UResult IsVoid;
//prefix
auto sCleaner(/*postfix*/CraftCleanup([](){})/*postfix*/);
// You SHOULD bind all parameters for the sake of portability.
// You MUST do this in one line for the sake of portability.
return(TestImplementation<FFunction, result_type>(fFunction, (IsVoid())));
}
/*****************************************************************/
/*****************************************************************/
/*****************************************************************/
void Tester1()
{
cout << 1 << '\n';
}
int Tester2()
{
cout << 2 << '\n';
return(2);
}
int main()
{
auto sFunction1 = [](){cout << 1 << '\n';};
auto sFunction2 = [](){cout << 2 << '\n'; return(2);};
TestInterface(sFunction1);
cout << '\n';
TestInterface(Tester1);
cout << '\n';
TestInterface(sFunction2);
cout << '\n';
TestInterface(Tester2);
cout << '\n';
TestWrapped(sFunction1);
cout << '\n';
TestWrapped(Tester1);
cout << '\n';
TestWrapped(sFunction2);
cout << '\n';
TestWrapped(Tester2);
cout << '\n';
TestCleanup(sFunction1);
cout << '\n';
TestCleanup(Tester1);
cout << '\n';
TestCleanup(sFunction2);
cout << '\n';
TestCleanup(Tester2);
cout << '\n';
return(0);
}