简体   繁体   English

嵌套模板函数作为参数

[英]Nested Template Functions as Parameters

In Python, there is a very simply way of decorating a function such that you can add additional functionality before and/or after a function. 在Python中,有一种非常简单的装饰函数的方法,您可以在函数之前和/或之后添加其他功能。 In its most simple form, it looks like this: 最简单的形式如下:

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"

I wanted to write something with similar functionality in C++. 我想用C ++编写具有类似功能的东西。 I would like to pass a function with arbitrary parameters and return types into a function and have it perform some action. 我想将具有任意参数的函数传递给函数,然后将类型返回给函数,并使其执行某些操作。 This is what I came up with: 这是我想出的:

#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

This is working rather well. 这工作得很好。 For example, I can time mostly any function with the following: 例如,我可以使用以下命令计时几乎所有功能:

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;
}

The trouble arises when I try to time a templated function such as std::sort directly. 当我尝试直接计时诸如std::sort的模板化函数时,就会出现问题。 I can only do it if I specify the exact type. 如果指定了确切的类型,则只能这样做。 I supposed I am wondering if C++ is capable of doing nested template deduction. 我以为我想知道C ++是否能够执行嵌套模板推演。 I want it to deduce which form of std::sort I am using and change the implementation of t_sort dynamically: 我希望它推断出我正在使用哪种形式的std :: sort并动态更改t_sort的实现:

What I am currently doing: 我目前正在做什么:

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());
}

What I would like: 我想要的是:

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());
}

Is this possible? 这可能吗? My initial reaction is probably not, but if not, why? 我最初的反应可能不是,但是如果没有,为什么?

std::sort is not a function but a function template, std::sort不是函数而是函数模板,

One solution is to any accept Functor instead: 一种解决方案是改为接受任何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);
    }
}

And then call it: 然后调用它:

// 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 演示版

It seems like you've found your answer that something doesn't exist in c++ quite like what python provides, but I'd like to address your question of 'why?' 似乎您已经找到答案,就像python提供的东西一样,c ++中并不存在某些东西,但是我想解决您的“为什么?”问题 in the context of variadic templates. 在可变模板中。

The error message you're probably getting is "cannot determine which instance of overloaded function "std::sort" is intended" and that's because you aren't actually passing a specific function pointer to timer . 您可能收到的错误消息是“无法确定打算使用哪个重载函数实例“ std :: sort”” ,这是因为您实际上并未将特定的函数指针传递给timer Sort only becomes a specific function when you declare its type, as an un-specialized template it's just the idea of a function. 当您声明其类型时, Sort仅成为特定的函数,作为非专用模板,它只是函数的概念。 The compiler will deduce the types for T and Args in timer based only on the specific function and arguments you use, so a specific function is necessary. 编译器将仅根据特定的函数和您使用的参数推导timer TArgs的类型,因此特定的函数是必需的。 You've got this nice dynamic-typing idea of delaying that specification until later when you actually pass the arguments to t_sort . 您已经有了一个很好的动态类型化想法,可以将规范延迟到实际将参数传递给t_sort The compiler needs to know what specialized timer should be used when you initialize it with the constructor. 编译器需要知道在使用构造函数进行初始化时应使用哪种专用计时器。

A way to get the types infered for sort could be to pass everything to the timer constructor (delaying execution execute of the timer for later). 获取推断为sort的类型的sort可能是将所有内容传递给timer构造函数(延迟timer执行执行以供以后使用)。 Surely the types for sort can be inferred from something like this: 可以肯定地从以下类型推断出sort类型:

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

where the factory looks like 工厂的样子

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...);
}

We'd like the compiler to look inside the timer method and see that the Args... are of type std::vector<unsigned int>::iterator so clearly that is what should be used for the inputs to the function pointer which means that sort should be specialized on std::vector<unsigned int>::iterator . 我们希望编译器在timer方法内部查看,并看到Args...的类型为std::vector<unsigned int>::iterator因此很清楚,应该将其用作函数指针的输入,表示sort应专门针对std::vector<unsigned int>::iterator When the compiler looks at the method signature, it associates the Args... parameter to the iterator types and sees that the function needs to take those same types. 当编译器查看方法签名时,它将Args...参数与迭代器类型相关联,并看到函数需要采用相同的类型。 And this works! 这有效!

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)...); // func(std :: get(m_args)...); //more on this later time = gettime() - start; //稍后会有更多信息= gettime()-开始;

        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)...);
}

and then you could totally use 然后你可以完全使用

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

Okay, so this compiles with MSVC (VS2015) and the goal of using std::sort without specifying parameters is achieved. 好的,因此可以使用MSVC(VS2015)进行编译,并且可以达到使用std :: sort而不指定参数的目标。 There is a catch though and that's the evaluation of func (which I commented out). 不过有一个问题,那就是func的评估(我对此进行了评论)。 For some reason I get error C3546: '...': there are no parameter packs available to expand which seems to have been due to a bug in earlier version of the compiler. 由于某种原因,我收到error C3546: '...': there are no parameter packs available to expand ,这似乎是由于早期版本的编译器中的错误所致。 Some sources seem to say it's been fixed others say it's only on the roadmap. 一些消息来源似乎说它已经得到修复,而另一些消息者说它只是在路线图上。 I think figuring this out is outside the scope of the question, which was essentially about passing std::sort to a factory function that produced a lazy evaluating wrapper. 我认为弄清楚这一点超出了问题的范围,该问题本质上是关于将std :: sort传递给生成了惰性评估包装器的工厂函数。 I hope this is okay for now, if you can't figure out how to get around the compiler error maybe try posting another question. 我希望现在可以,如果您不知道如何解决编译器错误,请尝试发布另一个问题。 Also, maybe try using some other compilers, I didn't do that. 另外,也许尝试使用其他一些编译器,但我没有这样做。

A final thought, there might be another way to do what I've outlined by binding the parameters to the function within the template, making sort explicitly specialized. 最终的想法是,可以通过将参数绑定到模板中的函数,使sort显式专用化,来实现我概述的方法。 I think the pattern would more-or-less follow what I've written though, with the only differences being within the timer_s struct. 我认为该模式将或多或少地遵循我所写的内容,唯一的区别是在timer_s结构中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM