首页
学习
活动
专区
圈层
工具
发布

C++17中
EN

Stack Overflow用户
提问于 2019-02-14 07:03:35
回答 1查看 276关注 0票数 3

我正在用最新的C++技术制作一条类似于装潢器的巨蟒。我已经在这里看到了一些解决方案(Python-like C++ decorators),但我不知道是否可以做得更好。在其他人(Constructing std::function argument from lambda)的帮助下,我想出了以下解决方案。

代码语言:javascript
复制
template<typename TWrapped>
auto DurationAssertDecorator(const std::chrono::high_resolution_clock::duration& maxDuration, TWrapped&& wrapped)
{
    return [wrapped = std::forward<TWrapped>(wrapped), maxDuration](auto&&... args)
    {
        const auto startTimePoint = std::chrono::high_resolution_clock::now();

        static_assert(std::is_invocable<TWrapped, decltype(args)...>::value, "Wrapped object must be invocable");

        if constexpr (!(std::is_void<decltype(wrapped(std::forward<decltype(args)>(args)...))>::value))
        {
            // return by reference will be here not converted to return by value?
            //auto result = wrapped(std::forward<decltype(args)>(args)...);

            decltype(wrapped(std::forward<decltype(args)>(args)...)) result = wrapped(std::forward<decltype(args)>(args)...);

            const auto endTimePoint = std::chrono::high_resolution_clock::now();
            const auto callDuration = endTimePoint - startTimePoint;
            assert(callDuration <= maxDuration);

            return result;
        }
        else
        {
            wrapped(std::forward<decltype(args)>(args)...);

            const auto endTimePoint = std::chrono::high_resolution_clock::now();
            const auto callDuration = endTimePoint - startTimePoint;
            assert(callDuration <= maxDuration);
        }
    };
}

我不使用下面的“自动”,以确保返回类型是我所期望的(或至少兼容)。

我将能够使用它与任何可调用:无状态lambda,状态lambda,结构函子,函数指针,std::函数

代码语言:javascript
复制
std::function<double(double)> decorated = DurationAssertDecorator(1s, [](const double temperature) { return temperature + 5.0; });
double a = decorated (4);

构图也可以:

代码语言:javascript
复制
std::function<double()> wrapped = LogDecorator(logger, [] { return 4.0; });
std::function<double()> wrapped_wrapped = DurationAssertDecorator(1s, functor);

这将不是OK - int文本5是不可调用的:

代码语言:javascript
复制
std::function<void(double)> decorated = DurationAssertDecorator(1s, 5);

然而,到目前为止,它确实起到了作用:

  • 这种情况--包装的函数有一个返回值--我不确定是否只是自动获得结果,而包装的返回值是引用。如果是这样的话,就会发生一个副本,而不是保留引用(按指针和值返回应该是可以的)。这就是为什么我想出了这个奇怪的结构。我能做得更好吗?
  • 还有哪些其他改进/修复是可能的?
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-02-15 06:32:19

我已经意识到,如果我在调用前和调用后活动中使用RAII对象,我可以大大简化代码。空和非空返回值处理不再是必要的。

代码语言:javascript
复制
template<typename TWrapped>
auto DurationAssertDecorator(const std::chrono::high_resolution_clock::duration& maxDuration, TWrapped&& wrapped)
{
    return [wrapped = std::forward<TWrapped>(wrapped), maxDuration](auto&&... args) mutable
    {
        static_assert(std::is_invocable<TWrapped, decltype(args)...>::value, "Wrapped object must be invocable");

        struct Aspect
        {
            // Precall logic goes into the constructor
            Aspect(const std::chrono::high_resolution_clock::duration& maxDuration)
                : _startTimePoint(std::chrono::high_resolution_clock::now())
                , _maxDuration(maxDuration)
            {}

            // Postcall logic goes into the destructor
            ~Aspect()
            {
                const auto endTimePoint = std::chrono::high_resolution_clock::now();
                const auto callDuration = endTimePoint - _startTimePoint;
                assert(callDuration <= _maxDuration);
            }

            const std::chrono::high_resolution_clock::time_point _startTimePoint;
            const std::chrono::high_resolution_clock::duration& _maxDuration;
        } aspect(maxDuration);

        return wrapped(std::forward<decltype(args)>(args)...);
    };
}

它适用于正常用例:

代码语言:javascript
复制
auto wrappedFunctor = DurationAssertDecorator(1s, [](const double temperature)  { return temperature; });

我还想和非const函子一起工作,比如可变的lambdas:

代码语言:javascript
复制
auto wrappedFunctor = DurationAssertDecorator(1s, 
    [firstCall = true](const double temperature) mutable
    {
        if (firstCall)
        {
            firstCall = false;
            return temperature;
        }
        std::this_thread::sleep_for(2s);
        return temperature;
    });

所以我对这个解决方案很满意。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54684823

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档