move-"cериализация" шаблонных параметров
От: tapatoon  
Дата: 12.03.23 11:20
Оценка:

Есть библиотека (service). Клиенты реализуют колбэк-интерфейс передачи данных (handler). Параметры для передачи передаются библиотеке, которая дёргает колбэк-интерфейс.

Хочется такой интерфейс сервиса библиотеки:
service::send(T&&... args);


И такой интерфейс колбэка (реализуется клиентом)
handler::on_send(T&&... args);


  Ничего, кроме конверта в void* не придумал

//////////////////////////////////////////////
// Код библиотеки

struct Params
{
    template<typename... Args>
    std::tuple< Args... >* Get()
    {
        return reinterpret_cast< std::tuple< Args... >* >( p );
    };

    void* p;
};

struct AbstractHandler
{
    virtual ~AbstractHandler() {}
    virtual void func( Params& ) = 0;
};

template < typename ResultT, typename... Args >
struct Function
{
    using type = std::function< ResultT( Args... ) >;
};

template< typename T >
struct HandlerBase : AbstractHandler
{
    typename Function< void, Params& >::type userFuncCaller;

    template< typename userFuncResultT, typename... userFuncArgs >
    HandlerBase( std::function< userFuncResultT( T*, userFuncArgs... )> userFunc )
    {
        userFuncCaller = [ this, userFunc ]( Params& p ){
            std::tuple< userFuncArgs... > args;
            if( p.p )
            {
                args = *p.Get< userFuncArgs... >();
                std::apply( [ this, userFunc ]( auto &&... args ) { userFunc( reinterpret_cast< T* >( this ), args... ); }, args );
            }
        };
    }

    void func( Params& p ) override
    {
        userFuncCaller( p );
    }
};

struct Caller
{
    Caller()
    {
        handler.reset( new UserHandler() );
    }

    template< typename... Args >
    void CallFunc( Args&&... args )
    {
        Params params;
        params.p = new std::tuple< Args... >( std::move( args... ) );

        handler->func( params );
    }

    std::unique_ptr< AbstractHandler > handler;
};

//////////////////////////////////////////////
// Клиентский код

struct UserHandler : HandlerBase< UserHandler >
{
    // необходимость таким образом передавать функцию удручает
    UserHandler() : HandlerBase< UserHandler >( std::function< void( UserHandler*, int ) >( &UserHandler::userFunc ) )
    {}

    void userFunc( int i )
    {
        BOOST_TEST_MESSAGE( Format( L"IN USER FUNC - %1%"_sv, i ) );
        return;
    }
};

void main()
{
    Caller call;
    call.CallFunc( 5 );
}


Будут ещё мнения как это сделать?
Спасибо
Центр ИПсО Сил Специальных Операций
Отредактировано 12.03.2023 12:02 tapatoon . Предыдущая версия . Еще …
Отредактировано 12.03.2023 12:01 tapatoon . Предыдущая версия .
Re: move-"cериализация" шаблонных параметров
От: vopl Россия  
Дата: 12.03.23 12:26
Оценка:
Здравствуйте, tapatoon, Вы писали:

T>

T>Есть библиотека (service). Клиенты реализуют колбэк-интерфейс передачи данных (handler). Параметры для передачи передаются библиотеке, которая дёргает колбэк-интерфейс.

T>Хочется такой интерфейс сервиса библиотеки:

T>
T>service::send(T&&... args);
T>


T>И такой интерфейс колбэка (реализуется клиентом)

T>
T>handler::on_send(T&&... args);
T>


T>[cut=Ничего, кроме конверта в void* не придумал]


#include <memory>
#include <cassert>
#include <iostream>

//////////////////////////////////////////////
// Код библиотеки

struct AbstractHandler
{
    virtual ~AbstractHandler() {}
    virtual void func( int&& p1, std::string&& p2 ) = 0;
};

//////////////////////////////////////////////
// Клиентский код

struct UserHandler : AbstractHandler
{
    void func( int&& p1, std::string&& p2 ) override
    {
        std::cout << "IN USER FUNC - " << p1 <<", " << p2 << std::endl;
        p2.clear();
    }
};

//////////////////////////////////////////////
// Код библиотеки

struct Caller
{
    Caller()
    {
        handler.reset( new UserHandler() );
    }

    void CallFunc( int&& p1, std::string&& p2 )
    {
        handler->func( std::move(p1), std::move(p2) );
    }

    std::unique_ptr< AbstractHandler > handler;
};

int main()
{
    Caller call;
    std::string p2{"five"};
    call.CallFunc( 5, std::move(p2) );
    assert(p2.empty());
}
Re: move-"cериализация" шаблонных параметров
От: rg45 СССР  
Дата: 12.03.23 13:41
Оценка: 4 (2)
Здравствуйте, tapatoon, Вы писали:

T>

T>Есть библиотека (service). Клиенты реализуют колбэк-интерфейс передачи данных (handler). Параметры для передачи передаются библиотеке, которая дёргает колбэк-интерфейс.

T>Хочется такой интерфейс сервиса библиотеки:

T>
T>service::send(T&&... args);
T>


T>И такой интерфейс колбэка (реализуется клиентом)

T>
T>handler::on_send(T&&... args);
T>


T>Ничего, кроме конверта в void* не придумал


T>Будут ещё мнения как это сделать?

T>Спасибо

Лет восемь назад я разработал одну чудо-утилитку, которая, если не в точности, то концептуально очень близка к тому, что тебе нужно. В основе утилиты лежит очень необычный механизм связывания формальных и фактических параметров произвольных вызываемых сущностей. Привычная схема: каждая вызываемая сущность задает какой-то список формальных параметров (фиксированный или переменный), а мы имеем позможность передавать ей различные фактические параметры. В том механизме, о котором рассказываю я, все наоброт: задан произвольный неупорядоченный набор ФАКТИЧЕСКИХ параметров (я назыаю это контекстом). И на одном и том же множестве фактических параметров можно вызывать различные вызываемые сущности, отличающиеся количеством и типом формальных параметров. Для каждой вызываемой сущности утилита выбирает из контекста набор максимально подходящих фактических параметров и выполняет вызов. Связывание формальных и фактических параметров происходит в компайл-тайме и в случае, когда фактический параметр подобрать не удается, возникает ошибка компиляции. Более простые функционалы можно объединять в более сложные. Концептуально примерно так:

auto f = combine(
   [](int i) { std::cout << i << " | "; },
   [](const std::string& s) { std::cout << s << " | "; },
   [](int i, const std::string& s) { std::cout << i, " " << s << " | "; },
   [](const std::string& s, double d) { std::cout << s << " " << d; }
);

f("Hello", 3.14, 42); // -> 42 | Hello | 42 Hello | Hello 3.14

f(3.14, 42); // -> error: could not find actual parameter convertible to const std::string&


Использование этого механизма может быть вложенным, благодаря чему и сами контексты, и обработчики приобретают динамическую древовидную структуру. На базе этого очень быстро и легко можно разрабатывать специфические DSL, которые делают разработку в любой прикладной области декларативной и очень наглядной. Внутри и средствами языка С++ можно создавать специфические высокоуровневые описательные языки. Правда, у нас это используется пока только для разработки юнит тестов, но в принцие, область применения ни чем не ограничена. Код утилиты не выкладываю, во-первых, потому, что это более 300 строк кода, а во-вторых потому, что она давно уже перестала быть моей собственностью.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 12.03.2023 17:04 rg45 . Предыдущая версия . Еще …
Отредактировано 12.03.2023 14:22 rg45 . Предыдущая версия .
Отредактировано 12.03.2023 14:08 rg45 . Предыдущая версия .
Отредактировано 12.03.2023 13:58 rg45 . Предыдущая версия .
Отредактировано 12.03.2023 13:49 rg45 . Предыдущая версия .
Отредактировано 12.03.2023 13:46 rg45 . Предыдущая версия .
Отредактировано 12.03.2023 13:43 rg45 . Предыдущая версия .
Re[2]: move-"cериализация" шаблонных параметров
От: tapatoon  
Дата: 12.03.23 13:43
Оценка:
Здравствуйте, vopl, Вы писали:

V>struct AbstractHandler

V>{
V> virtual ~AbstractHandler() {}
V> virtual void func( int&& p1, std::string&& p2 ) = 0;
V>};
Не, это базовый интерфейс для всех клиентов, тут любые параметры должны уметь принимать
Центр ИПсО Сил Специальных Операций
Re[2]: move-"cериализация" шаблонных параметров
От: tapatoon  
Дата: 12.03.23 14:00
Оценка:
Здравствуйте, rg45, Вы писали:

R>все наоброт: задан произвольный неупорядоченный набор ФАКТИЧЕСКИХ параметров (я назыаю это контекстом). И на одном и том же множестве фактических параметров можно вызывать различные вызываемые сущности, отличающиеся количеством и типом формальных параметров

Думал в этом направлении, для каждого набора параметров можно во время компиляции задать какой-нибудь id. Но для этого нужно всех их собрать в один cpp, насколько я понимаю.
Центр ИПсО Сил Специальных Операций
Re[3]: move-"cериализация" шаблонных параметров
От: rg45 СССР  
Дата: 12.03.23 14:19
Оценка:
Здравствуйте, tapatoon, Вы писали:

T>Думал в этом направлении, для каждого набора параметров можно во время компиляции задать какой-нибудь id. Но для этого нужно всех их собрать в один cpp, насколько я понимаю.


У нас получается так, что большая предметная облась поделена на более мелкие области в каждой из который создается свой специфический язык для описания тестовых сценариев. Эти описательные языки доступны через отдельные заголовочные файлы и используются в разных единицах трансляции и даже в разных проектах. Элементы этих описательных языков не обязательно монолитны — общий набор языковых элементов верхнего уровня может расширяться на уровне отдельных тест сьютов.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[3]: move-"cериализация" шаблонных параметров
От: vopl Россия  
Дата: 12.03.23 15:16
Оценка: 1 (1)
Здравствуйте, tapatoon, Вы писали:

T>Здравствуйте, vopl, Вы писали:


V>>struct AbstractHandler

V>>{
V>> virtual ~AbstractHandler() {}
V>> virtual void func( int&& p1, std::string&& p2 ) = 0;
V>>};
T>Не, это базовый интерфейс для всех клиентов, тут любые параметры должны уметь принимать

что то теперь не вполне понятно, что именно тебе надо .. Как то раз делал штуку, которая проводит вызов по аналогии boost::signals, но в рантайме обеспечивает нечто типа perfect forwarding. Получается, что если ее активировать с правой ссылкой и пользовательский калбек способен принять правую ссылку — будет перемещение параметра, иначе — копирование. Внутре у ней неонка стирание типа пользовательского калбека, поэтому прекрасно работает и сквозь границу единицы трансляции, и сквозь границу dll/so. Тут рафинированный пример использования, смотри кейс "move — move"
https://github.com/vopl/dci-core-sbs/blob/master/test/transfer.cpp#L71

Если такое тебе надо — могу порасказывать, как оно устроено и как повторить малыми силами и без лишнего
Отредактировано 12.03.2023 15:18 vopl . Предыдущая версия .
Re: move-"cериализация" шаблонных параметров
От: B0FEE664  
Дата: 13.03.23 11:50
Оценка:
Здравствуйте, tapatoon, Вы писали:

T>

T>Есть библиотека (service). Клиенты реализуют колбэк-интерфейс передачи данных (handler). Параметры для передачи передаются библиотеке, которая дёргает колбэк-интерфейс.

Вам нужны шаблонные виртуальные функции? Их всё ещё не завели в С++.

T>Хочется такой интерфейс сервиса библиотеки:

T>
T>service::send(T&&... args);
T>


T>И такой интерфейс колбэка (реализуется клиентом)

T>
T>handler::on_send(T&&... args);
T>


T>[cut=Ничего, кроме конверта в void* не придумал]

T>[ccode]

Т.е. колбэк будет вызываться ровно один раз на каждый service::send ?
А не проще тогда передавать параметры в замыкании лямбды, которую (обернув в std::function) мувить по коду?

Или вы с этими args производите манипуляции?
И каждый день — без права на ошибку...
Re[2]: move-"cериализация" шаблонных параметров
От: tapatoon  
Дата: 16.03.23 05:58
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Т.е. колбэк будет вызываться ровно один раз на каждый service::send ?

BFE>А не проще тогда передавать параметры в замыкании лямбды, которую (обернув в std::function) мувить по коду?
Ага, пока это рабочий вариант

BFE>Или вы с этими args производите манипуляции?

Вот да, и это проблема. Параметры ещё как-то используются. Да ещё и в евентах передаются (Код уже написан, вместо Args... там сейчас нечто вроде variant)
Центр ИПсО Сил Специальных Операций
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.