简体   繁体   中英

How to make a template Wrapper/Decorator in c++17

Dear Stackoverflow community,

I'm still bit fresh in c++ and I've been scratching my head and haven't found a solution to my problem yet. I've been searching and trying things for a while now and I've gotten to the point where asking a question would be more beneficial and educational.

Problem:

I'd like to make a class or function that wraps/decorates a given function with or without parameters. Like a good old fashioned @wrapthis in python or c# and the like.

The closest thing I found so far (that is elegant, short and easy to use) is from this stackoverflow answer: C++ Equivalent Decorator

The scratching-my-head part is trying to pass a pointer-function. The error I'm receiving:

Error (active) E0300 a pointer to a bound function may only be used to call the function

Which obviously means that somehow passing a pointer in this fashion is not allowed, so what are my options here?

A working example as answer would be great!

Example of what I'm trying to achieve can be found below:

Something.h

class Something
{
private:
public:
    Something() {}
    void v_func_with_nothing() { std::cout << "v_func_with_nothing" << "\n"; }
    void v_func_with_void(void) { std::cout << "v_func_with_void" << "\n"; }
    void v_func_with_one_arg(int x) { std::cout << "v_func_with_one_arg" << x << " " << "\n"; }
    void v_func_with_args(int x, int y) { std::cout << "v_func_with_args" << x << " " << y << "\n"; }

    int return_func_with_nothing() { return 1; }
    int return_func_with_void(void) { return 3; }
    int return_func_with_one_arg(int x) { return x; }
    int return_func_with_args(int x, int y) { return x+y; }
};

Decorator.h [Again source: C++ Equivalent Decorator ]

template<typename T>
auto decorator(T&& func)
{
    auto new_function = [func = std::forward<T>(func)](auto&&... args)
    {
        std::cout << "BEGIN decorating...\n";
        auto result = func(std::forward<decltype(args)>(args)...);
        std::cout << "END decorating\n";
        return result;
    };
    return new_function;
}

main.cpp

#include <iostream>
#include "Something.h"
#include "Decorator.h"

int main()
{
    Something* something = new Something();       
    auto somedeco = decorator(&something->return_func_with_one_arg);//<-- error here in argument   
    //int value = somedeco(**enter an argument**);
    //std::cout << value << "\n";  
    return 0;
}

Thank you!

EDIT: SOLUTION

Based on the kind answers given down below I thought of editing this post with an example. The solution to the problem was using lambda.

Decorator.h : I created 2 decorators (one for return-functions, one for void-functions):

template<typename T>
auto DECO_R(T&& func)
{
    try
    {
        auto new_function = [func = std::forward<T>(func)](auto&&... args)
        {
            std::cout << "BEGIN RETURN decorating...\n";
            auto result = func(std::forward<decltype(args)>(args)...);
            std::cout << "END RETURN decorating\n";
            return result;
        };
        return new_function;
    }
    catch (const std::exception& ex)
    {
        std::cout << ex.what() << "\n";
    } 
}

template<typename T>
auto DECO_V(T&& func)
{
    try
    {
        auto new_function = [func = std::forward<T>(func)](auto&&... args)
        {
            std::cout << "BEGIN VOID decorating...\n";
            func(std::forward<decltype(args)>(args)...);
            std::cout << "END VOID decorating\n";
        };
        return new_function;
    }
    catch (const std::exception& ex)
    {
        std::cout << ex.what() << "\n";
    }
}

Main.cpp : 2 examples

int main()
{
    Something* something = new Something();
    
    auto somedeco = DECO_R(
        [&](int x) {
            return something->return_func_with_one_arg(x);
        });
    
    int value = somedeco(255);
    std::cout << value << "\n";

    auto some_v_deco = DECO_V(
        [&](int x) {
            return something->v_func_with_one_arg(x);
        });

    some_v_deco(2);

    return 0;
}

Output

BEGIN RETURN decorating...
END RETURN decorating
255

BEGIN VOID decorating...
v_func_with_one_arg2
END VOID decorating

I hope this helps others out there.

The call decorator(&something->return_func_with_one_arg) is invalid. There's no such thing as a pointer to a bound function in C++.

If you want somedeco to be a function-like object that wraps a call to something->return_func_with_one_arg(42) , for example, you will need to wrap the call either in a lambda:

auto somedeco = decorator(
    [&]() {
        return something->return_func_with_one_arg(42);
    }
);
somedeco();

Or you could pass the parameter through the decorator:

auto somedeco = decorator(
    [&](int x) {
        return something->return_func_with_one_arg(x);
    }
);
somedeco(42);

Keep in mind that this will require that the object pointed to by something outlives the object returned by decorator .

There is no simple 1:1 replacement for Pythons dynamic typing in C++. Your example does not compile, because there are no pointer to member function of one specific instance. Pointer to member functions always need an instance to be called. For one simple case I would suggest to use a lambda:

int main() {
    Something something;
    auto somedeco = [&](auto param) {
        // before call
        auto v = something.return_func_with_one_arg(param);
        // after call
        return v;
    };
    return somedeco(1);
}

As you can see the whole machinery of decorate isn't really needed, because with a lamdda you can write the wrapped function inline. On the other hand, the decorator allows you to reuse // before call and // after call for different methods. To fix your code you could also pass the lambda to decorate .

PS: Don't use new to create objects.

you need bind it

int main()
{
    Something* something = new Something();       
    
    using std::placeholders::_1;
    auto f = std::bind( &Something::return_func_with_one_arg, something, _1 );
    auto somedeco = decorator( f );//<-- error here in argument   
    //int value = somedeco(**enter an argument**);
    //std::cout << value << "\n";  
    return 0;
}

https://godbolt.org/z/zdYW9q

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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