Following this amazing answer by Jarod on using SFINAE to make a function wrapper that behaves differently depending on the function's return type, I am having trouble incorporating into my data structure's code:
Code structure:
A directory called utils contains the 4
following files:
+ utils
| ---> wrap.hpp
| ---> wrap.cpp
| ---> exec.hpp
| ---> exec.cpp
These files contain the utilities logic to be used inside main
.
exec
deals with parsing astd::vector<std::string>
of tokens; a collection of words with together form a command which is interpreted by calling the relevant structure's function, with the relevant arguments.- The header contains a struct,
command
, which knows how many words it consists of, and holds the words themselves, as well as theexecute
function definition:
- The header contains a struct,
typedef struct command
{
uint8_t tokens_count;
vector<string> tokens;
command() : tokens_count(0U) { tokens.resize(4); }
}command;
// More on this function below.
void execute (command c);
- The
.cpp
is basically a big condition check that parses commands, like so:
void execute(command c)
{
switch (c.tokens_count)
{
// If the command consists of two words,
// It's one of the following:
case 2U:
if (c.tokens[0] == "GETSIZE")
{
if (c.tokens[1] == "MINHEAP")
{
// GETSIZE MINHEAP
auto out = wrapper([&](){ return minheap.getSize();});
}
else if (c.tokens[1] == "MAXHEAP")
{
// GETSIZE MAXHEAP
auto out = wrapper([&](){ return maxheap.getSize();});
}
else if (c.tokens[1] == "AVLTREE")
{
// GETSIZE AVLTREE
auto out = wrapper([&](){ return avl.getSize();});
}
// more stuff like these
Next, comes the wrapper:
wrap
contains the actual wrapper function-related logic:- The header contains definitions:
// remember to go and
// check my initial question @
// https://stackoverflow.com/questions/56685232/problems-trying-to-construct-a-wrapper-measuring-function-call-time
// to understand more about the logic behind the wrapper.
template<typename F, typename... Args>
auto wrapper(F function, Args&&... args) noexcept
-> std::enable_if_t<std::is_same<void, std::invoke_result_t<F, Args&&...>>::value, output>;
template<typename F, typename... Args>
auto wrapper(F function, Args&&... args) noexcept
-> std::enable_if_t<!std::is_same<void, std::invoke_result_t<F, Args&&...>>::value, output>;
- , and, the
.cpp
implementations:
template<typename F, typename... Args>
auto wrapper(F function, Args&&... args) noexcept
-> std::enable_if_t<std::is_same<void, std::invoke_result_t<F, Args&&...>>::value, output>
{
const high_resolution_clock::time_point t1 = timeNow();
function(forward<Args>(args)...);
const auto elapsed = duration(timeNow() - t1);
output out;
out.time = elapsed;
out.result = "";
return out;
}
template<typename F, typename... Args>
auto wrapper(F function, Args&&... args) noexcept
-> std::enable_if_t<!std::is_same<void, std::invoke_result_t<F, Args&&...>>::value, output>
{
const high_resolution_clock::time_point t1 = timeNow();
auto result = function(forward<Args>(args)...);
const auto elapsed = duration(timeNow() - t1);
output out;
out.time = elapsed;
out.result = to_string(result);
return out;
}
However, it turns out this is definetely not the way to do SFINAE magic:
In file included from util/exec.cpp:2:
util/wrap.hpp:55:6: error: function 'wrapper<(lambda at util/exec.cpp:38:25)>' is used but not defined in this translation unit, and cannot be defined in any other translation unit because its type does not have linkage
auto wrapper(F function, Args&&... args) noexcept
^
util/exec.cpp:38:17: note: used here
auto out = wrapper([&](){ return minheap.getSize();});
I went around and did the mandatory Google(or DDG)-fu, but didn't find anything touching on SFINAE/templates specifically.
What would the proper way to implement this wrapper function be, while, if possible, maintaining the code split across the files for better code structure and modularity/scalability?