2015-01-29 2 views
1

У меня есть эти строки в моем коде:Механизма расширяемого условного оператора

//lines in mycode.c++ 
    QString str = "...some id..."; 

     if(str == "int") 
      foo< int>() 
    else if(str == "QString") 
      foo< QString>() 
     ... 

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

Я представляю себе это так:

//A.h -- custom class 
class A { }; 

template< > 
    void foo< A>() { ... }; 

DECL(A, "A"); //macro to declare class 

Я хочу условный оператор в mycode.C++, которая бы автоматически принимать во внимание декларации класса , поэтому он будет иметь дополнительные строки:

else if(str == "A") 
    foo< A>() 

У меня мог бы быть такой эффект:

//common.h 
    void process_id(QString str) { 
     if(str == "int") 
      foo< int>() 
    else if(str == "QString") 
      foo< QString>() 
     ... 
    else if(str == "A") //this lines programmer put manually 
      foo< A>(); 
    } 

//mycode.c++ 
    #include "common.h" 

    QString str = "some_id"; 

    process_id(str); 

но что, если программист забудет отредактировать common.h файл?

Я думал, может быть, использовать систему C-macros или как-то Qt-precompilation. Является ли это возможным?

+0

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

+0

Считаете ли вы использование фабрики/карты для отправки вызовов, а не 'if/else'? –

+0

Это чувствует все назад. Зачем использовать гигантский блок 'if', когда вы можете использовать отправку? –

ответ

3

Я хотел бы сделать что-то вроде этого:

void process_id(QString const & str) 
{ 
    auto it = g_actions.find(str); 
    if (it != g_actions.end()) 
     (it->second)(); //invoke action 
} 

И рамки для поддержки выше, реализуется как:

using action_t = std::function<void()>; 

std::map<QString, action_t> g_actions; //map of actions! 

#define VAR_NAME(x)  _ ## x 
#define DEFINE_VAR(x) VAR_NAME(x) 
#define REGISTER(type) char DEFINE_VAR(__LINE__) = (g_actions[#type] = &foo<type>,0) 

Теперь вы можете зарегистрировать любой класс, как:

//these lines can be at namespace level as well! 
REGISTER(A); 
REGISTER(B); 
REGISTER(C); 

А затем позвоните process_id() as:

process_id("A"); //invoke foo<A>(); 
process_id("B"); //invoke foo<B>(); 

Надеюсь, что это поможет.

См. this online demo.

+0

Ха, не уверен, почему я сделал свои действия «вектором» вместо «карты» ... – Barry

+0

Я думаю ** & foo ** должно быть ** & foo **. Должен ли я делать * g_actions * extern или что-то вроде этого? –

+0

@ ValentinT .: Да. Правильно. Отредактировал его, добавив онлайн-демонстрацию. – Nawaz

3

Я бы просто создать вектор функторов:

using ProcessFunc = std::function<bool(const QString&)>; 
std::vector<ProcessFunc> ids; 

void process_id(QString str) { 
    for (ProcessFunc& f : ids) { 
     if (f(str)) { 
      break; 
     } 
    } 

    // or... 
    std::any_of(ids.begin(), ids.end(), [&](const ProcessFunc& f){ 
     return f(str); 
    }); 
} 

Где вы просто предоставить метод для добавления нового таких ProcessFunc:

template <typename T> 
void register_class(const QString& name) { 
    ids.emplace_back([=](const QString& str) { 
     if (str == name) { 
      foo<T>(); 
      return true; 
     } 
     else { 
      return false; 
     } 
    }); 
} 

Ваш пример определенно будет:

register_class<int>("int"); 
register_class<QString>("QString"); 
register_class<A>("A"); 

Который, я полагаю, вы могли бы превратиться в макрос, если хотите.

+0

И ** ids ** вектор был бы глобальным? –

+0

@ ValentinT. Он должен быть доступен везде, где 'process_id' живет. Если это класс, то частный член имеет смысл. – Barry