Генерация вызовов функций с использованием комбинаций из двух списков - PullRequest
0 голосов
/ 28 января 2019

Я хочу протестировать множество различных типов сообщений, где каждый тип сообщения может содержать различные типы объектов.У меня есть

template <typename ObjectT> class MessageRequest1;
template <typename ObjectT> class MessageReply1;
...
template <typename ObjectT> class MessageRequestN;
template <typename ObjectT> class MessageReplyN;

И, аналогично, у меня есть множество типов объектов. У меня есть функции шаблонов, которые могут тестировать комбинации типов:

template <MessageType, ObjectType> bool TestFunction(void);

Что я хочу сделать,это система макросов (или что-то еще), которая может вызывать мою функцию TestFunction со всеми комбинациями MessageTypes и ObjectTypes.Я предполагаю что-то вроде:

ADD_MESSAGE_TYPE(1);
...
ADD_MESSAGE_TYPE(N);

ADD_OBJECT_TYPE(Object1);
...
ADD_OBJECT_TYPE(ObjectN);

, который будет генерировать вызовы TestFunction со всеми типами объектов для всех типов сообщений.Есть идеи, как этого добиться?

Ответы [ 2 ]

0 голосов
/ 28 января 2019

По какой-то причине @ Макс ответ не является нулевой стоимостью (генерирует инструкции test и je).Ниже моя версия с ++ 14 с нулевой стоимостью.https://gcc.godbolt.org/z/kwgMZK

#include <type_traits>

//User Objects
template<class Obj>
struct Msg1;
template<class Obj>
struct Msg2;
template<class Obj>
struct Msg3;

struct Obj1;
struct Obj2;
struct Obj3;
struct Obj4;

template<class MsgType, class ObjType>
void TestFunction(void);

namespace helper {
    template<template<class Obj> class Msg>
    struct TMsg {
        template<class O>
        using RMsg = Msg<O>;
    };

    template<class... Type>
    struct Wrap{};

    template<class Msg>
    void caller1(Msg, Wrap<>){}

    template<class Msg, class Obj, class... Objs>
    void caller1(Msg m, Wrap<Obj, Objs...> O) {
        Obj o;
        using TMsgL = typename std::remove_reference<decltype(*m)>::type;
        using ObjL = typename std::remove_reference<decltype(*o)>::type;
        using MsgL = typename TMsgL::template RMsg<ObjL>;
        TestFunction<MsgL, ObjL>();
        Wrap<Objs...> r;
        caller1(m, r);
    }

    template<class... Objs>
    void caller(Wrap<>, Wrap<Objs...>){}

    template<class Msg, class... Msgs, class... Objs>
    void caller(Wrap<Msg, Msgs...> M, Wrap<Objs...> O){
        Msg m;
        caller1(m, O);
        Wrap<Msgs...> ML;
        caller(ML, O);
    }
}

void foo(){
    using Msgs = helper::Wrap<helper::TMsg<Msg1>*, helper::TMsg<Msg2>*, helper::TMsg<Msg3>*>;
    using Objs = helper::Wrap<Obj1*, Obj2*, Obj3*, Obj4*>;
    Msgs m;
    Objs o;
    caller(m, o);
}

Генерируемая сборка

foo():
        sub     rsp, 8
        call    void TestFunction<Msg1<Obj1>, Obj1>()
        call    void TestFunction<Msg1<Obj2>, Obj2>()
        call    void TestFunction<Msg1<Obj3>, Obj3>()
        call    void TestFunction<Msg1<Obj4>, Obj4>()
        call    void TestFunction<Msg2<Obj1>, Obj1>()
        call    void TestFunction<Msg2<Obj2>, Obj2>()
        call    void TestFunction<Msg2<Obj3>, Obj3>()
        call    void TestFunction<Msg2<Obj4>, Obj4>()
        call    void TestFunction<Msg3<Obj1>, Obj1>()
        call    void TestFunction<Msg3<Obj2>, Obj2>()
        call    void TestFunction<Msg3<Obj3>, Obj3>()
        add     rsp, 8
        jmp     void TestFunction<Msg3<Obj4>, Obj4>()
0 голосов
/ 28 января 2019

Мой подход состоял бы в том, чтобы хранить списки этих типов в классах-оболочках с вариациями, а затем использовать выражения сгиба для генерации вызовов функций для каждой комбинации типов:

template<class ... Ts>
struct wrapper
{};

template<template<class> class ... Ts>
struct templateWrapper
{};


using ObjectTypes = wrapper<
    Object1,
    //...
    ObjectN
    >;

using MessageTypes = templateWrapper<
    MessageRequest1,
    MessageReply1,
    //...
    MessageRequestN,
    MessageReplyN
    >;

template<class MessageType, class ObjectType>
bool TestFunction(void);

template<template<class> class MessageType, class ... ObjectTypes>
bool callForAllYall_helper2(wrapper<ObjectTypes...>*)
{
    return (TestFunction<MessageType<ObjectTypes>, ObjectTypes>() && ...);
}

template<template<class> class ... MessageTypes>
bool callForAllYall_helper1(templateWrapper<MessageTypes...>*)
{
    return (callForAllYall_helper2<MessageTypes>((ObjectTypes*)(nullptr)) && ...);
}

bool callTestFunctionForAllYall()
{
    return callForAllYall_helper1((MessageTypes*)(nullptr));
}

https://godbolt.org/z/Cj6cDS

Макросы не нужны!

Проверьте сгенерированную сборку, чтобы убедиться, что она действительно вызывает TestFunction<MessageType<ObjectType>, ObjectType> для каждой пары MessageType, ObjectTypeand отредактировал возвращаемые значения, так как это, вероятно, то, что вы хотите.Вместо этого вы можете заменить && на ,, чтобы отбрасывать (все, кроме последних) значения.

Дальнейшее абстрагирование и повторное использование одного и того же кода для различных тестовых функций на самом деле немного сложнее, потому что вы не можете передатьшаблонные функции в качестве параметров шаблона (только их экземпляры, что здесь не полезно).Вам нужно будет обернуть все ваши тестовые функции в структуру (или лямбду), которую можно передать как аргумент типа (и передать ее самой внутренней вспомогательной функции).

...