2017-02-02 2 views
0

У меня есть приложение на C++, где main() создает объект MyApp, а затем передает объект функции ReadConfig.Как вызвать функции C++ с различными сигнатурами с помощью указателя функции

ReadConfig открывает текстовый файл, анализирует его и вызывает соответствующие методы MyApp для его настройки.

class MyApp 
{ 
private: 

public: 
    void SetRate(uint16_t); 
    void EnableLogging(bool); 
    void SetAddress(uint32_t); 
}; 

Я пытаюсь сделать его легко поддерживать ReadConfig таким образом, что, как новые общественные методы добавляются MyApp, это может быть столь же просто, как обновить таблицу. Я придумал следующее решение, но мне это не нравится. Его трудно поддерживать из-за того, что я должен поставить 0s в нужное место.

Ниже приведен пример того, что я смог выработать. Любые советы, чтобы сделать это лучше, будут оценены. Заметьте, что я работаю в embedded, поэтому компиляторы C++, которые мы используем, не поддерживают C++. 14 & нет никакого повышения. И я хотел бы избегать использования каких-либо библиотек STL, чтобы понять, как это сделать сама механика.

Вот что у меня есть:

enum ARGTYPE {TBOOL, TUINT16, TUINT32}; 

template<typename TOBJ, typename TARG> 
struct TSetting 
{ 
    void (TOBJ::*FSet)(TARG); 
}; 

template<typename obj> 
struct SETTINGFN 
{ 
    const char      *setting_name; 
    ARGTYPE       Targ; 
    TSetting<obj,bool>    HBool; 
    TSetting<obj,uint16_t>   HUint16; 
    TSetting<obj,uint32_t>   HUint32; 
}; 

SETTINGFN<MyApp> MyAppSettings[] = 
{ 
    "logging" ,TBOOL,  &MyApp::EnableLogging, 0,0,0 
    ,"maxrate" ,TUINT16,  0,0,0,&MyApp::SetRate 
    ,"address" ,TUINT32,  0, &MyApp::SetAddress, 0,0 

}; 
unsigned int MyAppSettings_Count = sizeof(MyAppSettings)/sizeof(SETTINGFN<MyApp>); 

Тогда как я прочитал конфигурационный файл и разобрать его, вызвать функцию для обработки вызова фактических функций MyApp через указатели на функции. Эта функция такова:

bool AppSetting(MyApp &E, TObjnode &node) 
{ 
    bool rval = false; 

    for(unsigned int i=0; i<MyAppSettings_Count && !rval; i++) 
    { 
    if(node.GetName() == MyAppSettings[i].setting_name) 
    { 
     rval = true; 

     switch(MyAppSettings[i].Targ) 
     { 
     case TBOOL: 
      (E.*MyAppSettings[i].HBool.FSet)(node.GetValue().AsBool()); 
     break; 

     case TUINT16: 
      (E.*MyAppSettings[i].HUint16.FSet)(node.GetValue().Value()); 
     break; 

     case TUINT32: 
      (E.*MyAppSettings[i].HUint32.FSet)(node.GetValue().Value()); 
     break; 
     } 
    } 
    } 

    return(rval); 
} 
+0

Рассмотренных передавая строку каждому сеттера, и позволяя им вызывать типоспецифический повторно используемый код синтаксического анализа? Тогда все сеттеры могут быть одного типа. Батут, который принимает строку, и вызывает как функцию синтаксического анализа, так и тип безопасности, может быть еще лучше. –

+1

Итак, чтобы избежать необходимости добавлять вызов функции к вашей диспетчеру диспетчера конфигурации каждый раз при добавлении нового свойства/сеттера, вы хотите обновлять собственную структуру данных каждый раз при добавлении нового свойства/сеттера? Кажется, каждый из них ... –

+0

Я думал об этом, но я надеялся избежать изменения текущих сигнатур функций или добавления новых – Eric

ответ

2

Шип в вашем дизайне - это разные типы параметров.

Если параметры абстрагированы в структуру, то функции могут быть упрощены до одной фиксированной подписи.

Используя базовую структуру, подписи и быть более общим:

struct Arguments_Base 
{ 
}; 

void SetRate(Arguments_Base& ab); 
void EnableLogging(Arguments_Base& ab); 
void SetAddress(Arguments_Base& ab); 

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

Каждый тип аргументов должен быть выведен из класса Arguments_Base. Функция может затем dynamic_cast базовым классом.

Смотрите также: Фабрика шаблон дизайн, посетители Design Pattern, двойную диспетчеризация

+0

верьте этому или нет, я подумал об этом, но я надеялся избежать изменения текущих сигнатур функций или добавления новых, но это изящно. – Eric

+0

Yuck. dynamic_cast. Для чтения файла конфигурации. – rubenvb

+0

@ rubenvb динамический кастинг не понадобится, если базовый класс предоставил метод, который производные классы могли бы затем перегрузить. Подумайте о шаблоне проектирования NVI. В качестве альтернативы базовый класс может просто просто выполнить преобразование, если, как предлагает Бен Вейгт, Arguments_Base вместо этого является классом String, который может возвращать значения (ints, bools и т. Д.) – Eric

Смежные вопросы