1

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 a std::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.

    1. The header contains a struct, command, which knows how many words it consists of, and holds the words themselves, as well as the execute function definition:
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);
  1. 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:

    1. 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>;
  1. , 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?

xlxs4
  • 53
  • 2
  • 6
  • 1
    Also, don't duplicate every function you have to write to handle the void case, [do it this way](https://stackoverflow.com/a/56258868/2069064). – Barry Jun 20 '19 at 16:35
  • 1
    Or event better in this case would be to use `if constexpr`. – super Jun 20 '19 at 17:46

0 Answers0