简体   繁体   中英

C++ polymorphism - Auto detect derived type

I have certain code that I want to optimize. It looks like this:

function abc( string format ) {
  if (format == "a") { // this is a string, I shouldn't have used single quote, sorry for the confusion
    classx::a t;
    doit(t);
  }
  if (format == "b"){
    classx::b t;
    doit(t);
  }
  if (format == "c"){
    classx::c t;
    doit(t) 
  }
  if (format == "d"){
    classx::d t; 
    doit(t);
  }
}

Currently there is many doit() function with different type

function doit( classx:a ) {
   different code for a
}

function doit( classx:b ) {
   different code for b
}

...etc

As you can see, a lot of code is replicated. However I can't figure out how to reduce the words. Note that : doit(x) has overloaded by different type. a,b,c,d class is derived from a class named "X".

I may create a pointer type classx::X :

classx::X *t;
if (format == "a") t = new classx::a
if (format == "b") t = new classx::b
if (format == "c") t = new classx::c
if (format == "d") t = new classx::d
doit(*t)

but then still need to write a doit() for type classx::X with a bunch of "if then" and cast to the correct type... as C++ can't auto-detect and cast to correct type.

I wonder if there is a faster/smarter way to do this. Thanks in advance.

One possible approach that reduces the repetition to adding new entries to a function map:

template<class T> void innerAbc() {
    T t;
    doit(t);
}

typedef std::map<std::string, void (*)()> FuncMap;

FuncMap initHandlers() {
    FuncMap m;
    m["a"] = &innerAbc<classx::a>;
    // ... extend here
    return m;
}   

void abc(const std::string& format) {
    static const FuncMap handlers = initHandlers();
    FuncMap::const_iterator it = handlers.find(format);
    if (it != handlers.end()) 
        it->second();
}

Put the format/constructor pairs into a dictionary. The key is that format string, the value is a function pointer to a static factory method that is essentially just a thin wrapper over the constructor. Besides being easier to maintain, it'll do a hash lookup or binary search, depending on the sort of dictionary/map you use.

It will be faster if you use else if after the first if so that it doesn't keep testing after finding a match. This is more compact and simpler to read as well...

function abc(string format) {
    if (format == 'a')
        doit(classx::a());
    else if (format == 'b')
        doit(classx::b());
    else if (format == 'c')
        doit(classx::c())
    else if (format == 'd')
        doit(classx::d());
}

A simple template approach gets rid of much of the duplicated code. If you want to avoid having a series of 'if' statements, you can use a map, or a sorted vector with binary search.

template<typename T> void forward_doit()
{
    T t;
    doit(t);
}

void func(string const& s)
{
    if (s == "a") return forward_doit<Classx::a>();
    if (s == "b") return forward_doit<Classx::b>();
    if (s == "c") return forward_doit<Classx::c>();
    // ...
}

Here's an approach using macros that assumes that format is really a string. The single quotes you are using in the original (Javascript?) code are for characters.

I can't work out anything remotely as compact using templates, yes, sometimes macros are still useful!

#define FORMATTER(ltr) \
    if (format == #ltr) { \
    classx::##ltr t; \
    doit(t); \
  }

#define ELSEFORMATTER(ltr) else FORMATTER(ltr)

void abc( std::string format ) {
    FORMATTER(a)
    ELSEFORMATTER(b)
    ELSEFORMATTER(c)
    ELSEFORMATTER(d)
}

using boost preprocessor

#define MACRO(r, data, elem)                     \
if (format == '(elem)')  doit(classx::(elem)()); \
else

BOOST_PP_SEQ_FOR_EACH(MACRO, _, (a)(b)...) {
... // else condition
}

I am not sure how to put macro inside '' however: How to single-quote an argument in a macro?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM