试图构造包装器测量函数调用时间的问题

时间:2019-06-20 11:43:06

标签: c++ templates wrapper variant variadic

我正在实现一些数据结构,每个数据结构都支持一组命令,例如INSERT value

我使用了分词器来生成一个包含每个单词/值的向量。

我希望能够将每个函数调用花费的时间以及如果执行的函数 返回一些东西。

例如,如果命令为INSERT AVLTREE 4,我只想输出调用avl.insert(4)所花费的时间。 如果命令是SEARCH AVLTREE 4,我想输出调用avl.search(4)所花费的时间及其结果(例如"SUCCESS""FAILURE")。

以下代码可能有很多问题,但这是我想出的:

我制作了两个文件(.cpp / .hpp),其中包含以下自我攻击的函数包装器,一个变体以及一个结构:

// WRAPPER CPP
// file: wrap.cpp


#include "wrap.hpp"


#include <chrono>
#include <string>
#include <utility>

#include <boost/variant.hpp>


using std::chrono::high_resolution_clock;
using std::chrono::time_point;
using std::chrono::nanoseconds;

using std::string;
using std::to_string;

using std::forward;

using boost::get;
using boost::static_visitor;
using boost::apply_visitor;


// I'm overloading std::to_string, so it works on std::strings as well.
string to_string(const string &value)
{
    return value;
}

// I want to apply to_string on whatever is inside my variant.
class to_string_visitor : public static_visitor<>
{
    public:
        template <typename T>
        void operator()(T & operand) const
        {
            to_string(operand);
        }
};

// Takes two points in time and returns the time 
// between them in nanoseconds.
const nanoseconds::rep duration(const nanoseconds tpoints_difference) noexcept
{
    const auto result = tpoints_difference.count();
    return result;
}

// Generates a point in time.
const high_resolution_clock::time_point timeNow(void) noexcept
{
    const auto result = high_resolution_clock::now();
    return result;
}

// Here's where's the problematic magic happens:
// The ret boolean is set to true if the function F returns a value,
// otherwise, it is set to false.
//
// Variadic arguments are being taken and then std::forwarded to F.
template<typename F, typename... Args>
const output wrapper(bool ret, F function, Args&&... args) noexcept
{
        // Generate a point in time, t1.
    const high_resolution_clock::time_point t1 = timeNow();

        // If F returns a result,
    if (ret == true)
    {
                // assign it to result (my variant).
        result = function(forward<Args>(args)...);
    }
    else
    {
                // just call F with Args forwarded.
        function(forward<Args>(args)...);
    }

        // Generate another point in time, t2 and
        // count the difference between t2 - t1.
    const auto elapsed = duration(timeNow() - t1);

        // Make whatever is inside result a string
        // using std::to_string.
    apply_visitor(to_string_visitor(), result);

        // My struct
    output out;

        // which contains the time elapsed and
        // the result returned
    out.time = elapsed;
    out.result = get<string>(result);

        // I can theoretically use both time elapsed and
        // result returned however I want. Hooray!..almost:(
    return out;
}

以下是变体result

// These are all the types a data structure function may return.
variant<int, unsigned, uint32_t, size_t, graph_size, string> result = 0;

graph_size,仅供参考:

struct graph_size
{
    unsigned vertices; //Number of vertices that the Graph currently contains
    unsigned edges;    //Number of edges that the Graph currently contains
};

最后是output结构:

typedef struct output
{
    double time;          // function call time
    string result;        // what function returned

        // notice that if function returned nothing,
        // result will be an empty string.
    output() : time(0), result("") {}
} output;

我正试图像这样使用wrapper

AVL avl;
// stuff
auto out = wrapper(true, avl.insert, 4);

我收到以下错误:

invalid use of non-static member function 'void AVL::insert(int)'

这是一个额外的奖励,它应该向我提示我所喝的东西,但不能完全理解:

no matching function for call to 'wrapper(bool, <unresolved overloaded function type>, unsigned int&)'

有什么想法吗?

我非常感谢您所有的时间:)

编辑1:问题标题可能不太适合,如果您有一些好的想法,我会很高兴地更改

2 个答案:

答案 0 :(得分:0)

问题出在您的wrapper()函数使用情况中。使它与成员函数以及可能与void返回函数一起工作非常棘手(请参阅 Jarod42 的出色answer)。这个:

wrapper(true, avl.insert, 4);

由于多种原因而无法编译。不仅avl.insert是无效的,而且即使您试图通过指针传递成员函数并传递对象来调用它也是如此,

wrapper(true, &AVL::insert, avl, 4);

它仍然不会编译,但是由于如何在wrapper 中调用它。

解决方法是对这种工作使用合适的工具-std::invoke。它可以正确处理自由函数,成员函数等。用法示例:

#include <iostream>
#include <functional>

struct foo {
    void bar(int x) {
        std::cout << x;
    }
};

template <typename F, typename... Args>
void wrapper(F f, Args&&... args) {
    std::invoke(f, std::forward<Args>(args)...);
}

void free_bar() {
    std::cout << "free";
}

int main() {
    foo f;
    wrapper(&foo::bar, f, 1); // pass a pointer to member function and the instance itself, then arguments
    wrapper(free_bar);
}

请注意代码如何正确处理每种情况。 std::invoke只是做正确的事

答案 1 :(得分:0)

由于根据返回类型需要不同的行为,因此可以使用特殊化/ SFINAE,诸如:

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 = ""; // void return
    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;
}

可能的用法:

AVL avl;

auto out = wrapper([&](){ return avl.insert(4);});