C ++ 17中的Python样式装饰器

时间:2019-02-14 07:03:35

标签: c++ lambda stl c++17

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

var me = {
    name: 'Shivendra'
}
// promise creation
var myPromise = new Promise((resolve, reject) => {
    if (me) {
        resolve(me.name==='Shivendra')
    }
    else {
        reject(new Error("Oh no !"))
    }
}
)

function wrapper(){
    return myPromise;
}
// promise consumption
wrapper().then(res => {
    if (res) {
        console.log("Promised passed")
        console.log(me)
    }
    else {
        console.log("Promise was failed")
    }
})

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

我应该可以将其与任何可调用对象一起使用:无状态lambda,statefull lambda,结构函子,函数指针,std :: function

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

组成也可以:

std::function<double(double)> decorated = DurationAssertDecorator(1s, [](const double temperature) { return temperature + 5.0; });
double a = decorated (4);

这不行-整数5不可以被调用:

std::function<double()> wrapped = LogDecorator(logger, [] { return 4.0; });
std::function<double()> wrapped_wrapped = DurationAssertDecorator(1s, functor);

到目前为止,它确实可以解决问题,

  • 这种情况-包装的函数具有返回值-我不确定我是否只是通过自动获取结果,包装的返回值是否为引用。如果是这样,那么将进行复制而不是保留引用(按指针和按值返回应该可以)。这就是为什么我想到了这个奇怪的结构。我可以做得更好吗?
  • 还有哪些其他改进/修复方法?

1 个答案:

答案 0 :(得分:0)

我已经意识到,如果在呼叫前和呼叫后活动中使用RAII对象,则可以大大简化代码。无效和非无效返回值处理不再需要。

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

它适用于常规用例:

auto wrappedFunctor = DurationAssertDecorator(1s, [](const double temperature)  { return temperature; });

我还想将其与非常量函子(例如可变的lambda)一起使用:

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

所以我对此解决方案感到很满意。