Как создать шаблон для класса, который управляет массивом структур - PullRequest
0 голосов
/ 08 декабря 2018

Я хочу создать повторно используемый класс, который описывает массив структур, которые содержат указатель на метод в классе.Класс также содержит методы execute и show.Я думаю, что метод execute также должен быть шаблонным, но я все еще пытаюсь изучить шаблоны.

ВЫЗОВ: Как мне превратить этот класс в шаблон и предоставить пару примеров использования шаблона?

Этот класс объединяет typedef, struct и методы, которыми я хочу поделитьсячерез несколько классов.Каждый экземпляр будет иметь свой собственный массив struct, каждый из которых содержит указатель на строку и указатель на метод в классе.

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

Этот код представляет собой метод реализации простого языка командкоторый передает два кода (sel и act) в метод execute, который ищет в массиве совпадения по двум кодам и затем отправляет соответствующий метод.

Этот код также включает в себя два метода, которые также должны быть включеныв шаблоне: show (обходит массив и предоставляет справочную информацию для каждой команды) и execute, который вызывает соответствующую функцию.

class Commands {
public:
    typedef void ( Commands::*FunctionPointer )( char, char );


    struct command {
        char sel;
        char act;
        char const *desc;
        FunctionPointer funcPtr;
    };


    command myCommands [2] = {
        command { 'a','?',"Pass a and ? to foo", &Commands::foo },
        command { 'b','x',"Pass b and x to bar", &Commands::bar },
    };


    int cmdSize = sizeof ( myCommands ) / sizeof ( myCommands [0] );


    void foo ( char sel, char act ) {
        show ( { sel }, { act } );
    }


    void bar ( char sel, char act ) {
        show ( { sel }, { act } );
    }


    void show ( char sel, char act ) {
        //  sel and act are ignored vy this method
        for (int i = 0; i < cmdSize; i++) {
            Serial.print ( "SEL = " );
            Serial.print ( myCommands [i].sel );
            if (sel == myCommands [i].sel) Serial.print ( '*' );
            Serial.print ( ", ACT=" );
            Serial.print ( myCommands [i].act );
            if (act == myCommands [i].act) Serial.print ( '*' );
            Serial.print ( ' ' );
            Serial.println ( myCommands [i].desc );
        }
    }


    void execute ( char sel, char act ) {
        for (int i = 0; i < cmdSize; i++) {
            if (myCommands [i].sel == sel && myCommands [i].act == act) {
                Serial.println ( myCommands [i].desc );
                ( this->*myCommands [i].funcPtr )( sel, act );
                return;
            }
        }
        Serial.print ( F ( "Unknown SEL/ACT Pair:" ) );
        Serial.print ( sel );
        Serial.print ( '/' );
        Serial.println ( act );
    }
};

И эскиз Arduino:

#include "Commands.h"

Commands cmd;


void setup() {
    Serial.begin ( 115200 );
    cmd.show ( '?', '?' );
    Serial.println ( "EXECUTING:" );
    cmd.execute ( 'a', '?' );
    cmd.execute ( 'b', '?' );
    cmd.execute ( 'b', 'x' );
    Serial.println ( "DONE" );
}


void loop(){}

И, наконец, результат выполнения эскиза:

SEL = a, ACT=?* Pass a and ? to foo
SEL = b, ACT=x Pass b and x to bar
EXECUTING:
Pass a and ? to foo
SEL = a*, ACT=?* Pass a and ? to foo
SEL = b, ACT=x Pass b and x to bar
Unknown SEL/ACT Pair:b/?
Pass b and x to bar
SEL = a, ACT=? Pass a and ? to foo
SEL = b*, ACT=x* Pass b and x to bar
DONE

1 Ответ

0 голосов
/ 08 декабря 2018

Я хочу создать повторно используемый класс, который описывает массив структур, которые содержат указатель на метод в классе.Класс также содержит методы execute и show.Я думаю, что метод execute также должен быть шаблонным, но я все еще пытаюсь выучить шаблоны.

Я должен сказать, что не понимаю, что именно вы хотите делать.Однако, глядя на ваш код, кажется, что вы хотите std::map, а не массив (так что вы можете отобразить «команды» на указатели на функции).Если все методы, которые вы хотите вызвать, имеют одинаковую подпись (void(char,char)), вам не нужно много работать с шаблонами.Вместо этого я бы посоветовал вам взглянуть на std::function и лямбды.

#include <iostream>
#include <string>
#include <map>
struct command_center {
    using command = std::string;
    using comment = std::string;    
    using action = std::function< void(char,char) >;
    std::map< command,action > action_commands;
    void register_action_command( command c, action a) { 
        action_commands[c] = a; 
    }
    void execute( command c, char a,char b) { 
        auto it = action_commands.find(c);
        if (it == action_commands.end()) {
            std::cout << "command not found: " << c << "\n";
            return;
        }
        it->second(a,b);
    }
};

struct foo{
    void bar(char a,char b) { std::cout << a << b << " foo\n";}
};

int main(){
    command_center cc;
    cc.register_action_command("test1",[](char a,char b){ std::cout << a << b << "\n";});
    cc.execute("test1",'a','b');
    cc.execute("unknown",'a','b');
    foo f;
    cc.register_action_command("foobar",[&f](char a,char b){ f.bar(a,b); });
    cc.execute("foobar",'a','b');
}

Вывод:

ab
command not found: unknown
ab foo

Обратите внимание, что он может работать без lamdbasи без std::function, когда вы ограничиваете себя фиксированной подписью, то есть указателями на функции типа

using my_function_pointer_type = void(*)(char,char);

Однако, передав лямбду в std::function, вы можете обернуть любой вызываемый объект в правильную подпись и зарегистрировать ее.будет вызван позже (независимо от того, является ли это свободной функцией или каким-либо методом-членом, как в примере).

PS: Можно параметризовать вышеуказанное command_center для работы с указателями на функции другой сигнатуры.Только после написания этого ответа я понял, что, может быть, в этом и заключается вопрос?

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