嵌套模板函数作为参数

时间:2016-04-19 03:04:28

标签: python c++ templates

在Python中,有一种非常简单的方法来装饰一个函数,以便您可以在函数之前和/或之后添加其他功能。以最简单的形式,它看起来像这样:

from random import SystemRandom
from time import time
import functools

rdev = SystemRandom()

def time_function(func):
    @functools.wraps(func)
    def run(*args, **kwargs):
        start = time()
        ret = func(*args, **kwargs)
        print("Took {:0.5f}s".format(time()-start))
        return ret
    return run

@time_function
def foo():
    x = [rdev.randint(1, 1000) for _ in range(10000)]
    sorted(x)

foo()  # prints "Took 0.04239s"

我想在C ++中编写具有类似功能的东西。我想传递一个带有任意参数的函数,并将类型返回到一个函数中并让它执行一些操作。这就是我想出的:

#ifndef TIMEIT_H
#define TIMEIT_H
#include <string>
#include <functional>
#include <iostream>
#if defined(_WIN32)
#include <Windows.h>
namespace timeit {
    unsigned long gettime(void) {
        return GetTickCount();
    }
}
#elif defined(__linux__)
namespace timeit{
    unsigned long gettime(void) {
        return 0; // implement later
    }
}
#endif

namespace timeit {
    template <typename> struct timer_s;

    template<typename... Args> // this is required for any void function
    struct timer_s<void(Args ...)> {
        std::function<void(Args ...)> func;
        timer_s(std::function<void(Args ...)> f) : func{ f } {}
        void operator()(unsigned long &time, Args ... args) {
            unsigned long start = gettime();
            func(args ...);
            time = gettime() - start;
            return;
        }
    };

    template <typename T, typename... Args> // this is for any other return type
    struct timer_s<T(Args ...)> {
        std::function<T(Args ...)> func;
        timer_s(std::function<T(Args ...)> f) : func{ f } { }
        T operator()(unsigned long &time, Args ... args) {
            unsigned long start = gettime();
            T ret = func(args ...);
            time = gettime() - start;
            return ret;
        }
    };

    template<typename T, typename... Args>
    timer_s<T(Args...)> timer(T(*func)(Args ...)) {
        return timer_s<T(Args ...)>(std::function<T(Args ...)>(func));
    }
}
#endif//TIMEIT_H

这很有效。例如,我可以使用以下内容对大部分时间进行计时:

static std::random_device rdev;

unsigned int foo(size_t size){
    std::vector<unsigned int> nums(size);
    std::mt19937 rand(rdev());
    std::generate(nums.begin(), nums.end(), rand);
    std::sort(nums.begin(), nums.end());
    return nums.back(); // return largest number
}

int main(){
    //foo(0xffff); // normal call
    unsigned long foo_time = 0;
    auto t_foo = timeit::timer(foo);
    unsigned int largest = t_foo(foo_time, 0xffff); // stores time
    std::cout << "Took " << foo_time << "ms\nLargest number: " << largest << "\n";
    return 0;
}

当我尝试直接计算std::sort等模板化函数时会出现问题。如果我指定确切的类型,我只能这样做。我想我想知道C ++是否能够进行嵌套模板演绎。我希望它推断出我正在使用哪种形式的std :: sort并动态更改t_sort的实现:

我目前在做什么:

static std::random_device rdev;

int main(){
    std::vector<unsigned int> nums(size);
    std::mt19937 rand(rdev());
    std::generate(nums.begin(), nums.end(), rand);
    auto t_sort = timeit::timer(std::sort<std::vector<unsigned int>::iterator>);
    unsigned long sort_time = 0;
    t_sort(sort_time, nums.begin(), nums.end());
}

我想要的是什么:

static std::random_device rdev;

int main(){
    std::vector<unsigned int> nums(size);
    std::mt19937 rand(rdev());
    std::generate(nums.begin(), nums.end(), rand);
    auto t_sort = timeit::timer(std::sort); // this line is different
    unsigned long sort_time = 0;
    t_sort(sort_time, nums.begin(), nums.end());
}

这可能吗?我最初的反应可能不是,但如果没有,为什么?

2 个答案:

答案 0 :(得分:1)

std::sort不是函数,而是函数模板,

一种解决方案是接受任何接受Functor:

namespace timeit {

    template <typename Functor>
    struct timer_s {
        Functor func;
        double time = 0;
        timer_s(Functor  f) : func{ f } {}

        template <typename ... Ts>
        auto operator()(Ts ... args) {
            struct Finally {
                ~Finally() {
                    time = std::chrono::duration_cast<std::chrono::milliseconds>
                              (std::chrono::system_clock::now() - start).count();
                }
                double& time;
                std::chrono::time_point<std::chrono::system_clock> start;
            } finally{time, std::chrono::system_clock::now()};
            return func(std::forward<Ts>(args)...);
        }
    };

    template<typename F>
    timer_s<F> timer(F f) {
        return timer_s<F>(f);
    }
}

然后叫它:

// Generic lambda to wrap std::sort and call the correct one.
auto t_sort = timeit::timer([](auto b, auto e){ return std::sort(b, e); });

std::vector<int> v {4, 8, 23, 42, 15, 16};

t_sort(v.begin(), v.end());

Demo

答案 1 :(得分:0)

似乎你已经找到了答案,c ++中不存在与python一样的东西,但是我想解决你的'为什么?'的问题。在可变参数模板的上下文中。

你可能得到的错误信息是“无法确定哪个重载函数实例”std :: sort“意图”,那是因为你实际上没有传递一个特定的函数指针timerSort仅在声明其类型时才成为特定函数,作为非专用模板,它只是函数的概念。编译器将仅根据您使用的特定函数和参数推断TArgstimer的类型,因此需要特定的函数。你有一个很好的动态类型的想法,推迟说明,直到你实际将参数传递给t_sort。编译器需要知道在使用构造函数初始化时应该使用哪个专用计时器。

获取sort的类型的方法可能是将所有内容传递给timer构造函数(延迟执行执行定时器以供日后使用)。当然sort的类型可以通过以下方式推断出来:

auto t_sort = timeit::timer(std::sort, nums.begin(), nums.end());

工厂看起来像

template<typename T, typename... Args>
timer_s<T(Args...)> timer(T(*func)(Args ...), Args...)
{
    return timer_s<T(Args ...)>(std::function<T(Args ...)>(func), Args...);
}

我们希望编译器查看timer方法并看到Args...类型为std::vector<unsigned int>::iterator,所以很明显应该用于函数指针的输入,这意味着sort应专注于std::vector<unsigned int>::iterator。当编译器查看方法签名时,它会将Args...参数与迭代器类型相关联,并看到该函数需要采用相同的类型。这很有效!

template<typename ... Args> // this is required for any void function
struct timer_s<void(Args... )> {

    std::function<void(Args ...)> func;
    std::tuple<Args...> m_args;


    timer_s(std::function<void(Args ...)> f, Args... args)
        : func{ f }, m_args(std::forward<Args>(args)...)
    {
    }


    void operator()(unsigned long &time) {

        unsigned long start = gettime();

// func(std :: get(m_args)...); //稍后会详细说明             time = gettime() - start;

        return;
    }
};


template<typename... Args>
timer_s<void(Args...)> timerJBS(void(*func)(Args ...), Args... args)
{
    return timer_s<void(Args ...)>(std::function<void(Args ...)>(func), std::forward<Args>(args)...);
}

然后你可以完全使用

auto t_sort = timeit::timer(std::sort, nums.begin(), nums.end());

好的,所以这与MSVC(VS2015)进行编译,并且实现了在不指定参数的情况下使用std :: sort的目标。虽然有一个问题,那就是对func(我评论过)的评价。由于某些原因,我得到error C3546: '...': there are no parameter packs available to expand,这似乎是由于早期版本的编译器中的错误。一些消息来源似乎表示已经修复了其他人说这只是在路线图上。我认为解决这个问题超出了问题的范围,主要是将std :: sort传递给生成惰性评估包装器的工厂函数。我希望现在可以,如果你无法弄清楚如何绕过编译器错误,也许可以尝试发布另一个问题。此外,也许尝试使用其他一些编译器,我没有这样做。

最后的想法是,可能有另一种方法可以通过将参数绑定到模板中的函数来完成我所概述的内容,从而使sort显式专门化。我认为模式会或多或少地遵循我所写的内容,唯一的区别在于timer_s结构。