Как сделать шаблон Wrapper / Decorator на c ++ 17 - PullRequest
1 голос
/ 14 июля 2020

Уважаемое сообщество Stackoverflow,

Я все еще немного свободен sh в c ++, и я чесал голову и еще не нашел решения своей проблемы. Я уже некоторое время искал и пробовал что-то, и я дошел до того, что задать вопрос было бы более полезно и поучительно.

Проблема:

Я бы хотел создать класс или функцию, которая обертывает / украшает данную функцию с параметрами или без них. Как старый добрый @wrapthis в python или c# и т. Д.

Ближайшая вещь, которую я нашел до сих пор (элегантная, короткая и простая в использовании), взята из этого ответа stackoverflow: Эквивалентный декоратор C ++

Часть «почесывая голову» пытается передать функцию-указатель. Ошибка, которую я получаю:

Error (active) E0300 указатель на связанную функцию может использоваться только для вызова функции

Что, очевидно, означает, что каким-то образом указатель таким образом не разрешен, поэтому какие у меня здесь варианты?

Рабочий пример в качестве ответа был бы отличным!

Пример того, чего я пытаюсь достичь, можно найти ниже :

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 [Снова источник: Эквивалентный декоратор C ++ ]

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

Спасибо!

РЕДАКТИРОВАТЬ: РЕШЕНИЕ

Основываясь на добрых ответах, приведенных ниже, я подумал о том, чтобы отредактировать этот пост на примере. Решением проблемы было использование лямбда.

Decorator.h : я создал 2 декоратора (один для функций возврата, один для функций void):

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

Основной. cpp: 2 примера

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

Выход

НАЧАТЬ ВОЗВРАТ декорирование ... END RETURN украшение 255

НАЧАТЬ ОТКРЫТЬ ОТКРЫТЬ ... v_func_with_one_arg2 END VOID decorating

Надеюсь, это поможет другим.

Ответы [ 3 ]

2 голосов
/ 14 июля 2020

Вызов decorator(&something->return_func_with_one_arg) недействителен. В C ++ нет такой вещи, как указатель на связанную функцию.

Если вы хотите, чтобы somedeco был функционально-подобным объектом, который, например, завершает вызов something->return_func_with_one_arg(42), вам нужно будет обернуть вызов либо в лямбде:

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

Или вы можете передать параметр через декоратор:

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

Имейте в виду, что для этого потребуется, чтобы объект, на который указывает something переживает объект, возвращенный decorator.

2 голосов
/ 14 июля 2020

Не существует простой замены 1: 1 для Pythons Dynami c набора текста на C ++. Ваш пример не компилируется, потому что нет указателя на функцию-член одного указанного c экземпляра. Для указателя на функции-члены всегда требуется вызов экземпляра. В одном простом случае я бы предложил использовать лямбда:

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

Как видите, весь механизм decorate на самом деле не нужен, потому что с помощью lamdda вы можете написать встроенную функцию в оболочку. С другой стороны, decorator позволяет вам повторно использовать // before call и // after call для разных методов. Чтобы исправить свой код, вы также можете передать лямбду в decorate.

PS: не используйте new для создания объектов.

0 голосов
/ 14 июля 2020

нужно привязать

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...