1

I have a long and steadily growing list of (non-member) functions and I need to select one of the functions from this list at runtime (based on a command line argument). At the moment I do this using a factory function which takes a string (the name of the function) and returns a pointer to the function. However this means I have to edit the factory function every time I add a new function (which is both annoying and a violation of the DRY principle).

I would like to somehow generate the factory function by parsing the source code of the list of functions (inspired by reading the code generation section of The Pragmatic Programmer last night). However it seems that it is very hard to parse C++ correctly and I would rather not make my project depend on libclang just for this.

So my question is: how can I select a function by name at runtime without introducing heavyweight dependencies?

Is it "safe" to only partially parse the c++ code (e.g. just using a regex to extract function names by matching something that looks like a set of function arguments)?

Are there any other (portable and not too hacky) ways of doing this without manually writing the selection code?

edit: Forgot to say: I'm not allowed to use C++11, so no lambda functions unfortunately.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
dshepherd
  • 4,989
  • 4
  • 39
  • 46
  • 2
    Do all functions have the same signature ? – Quentin Aug 25 '14 at 15:06
  • 1
    use `map` (or funcptr typedef) to store the name and address. – SHR Aug 25 '14 at 15:09
  • @Quentin Yep, so it wouldn't be too hard too extract the function names... but is it a good idea? – dshepherd Aug 25 '14 at 15:13
  • @SHR That's better than what I'm doing now. However it still requires writing out the function names in multiple places, right? – dshepherd Aug 25 '14 at 15:14
  • @dshepherd You should init the map, You can wrap it in a singleton class, and for each file you can add the functions from the file to the map. – SHR Aug 25 '14 at 15:22
  • Just an idea but if those list of functions could be converted as dll files, then I think the calling function could be generalized. – Keugyeol Aug 25 '14 at 15:22
  • 2
    you can take a look how google-gtest or other frameworks do it using automatic class registration at link time (i.e. http://stackoverflow.com/q/9975672/847349, http://stackoverflow.com/q/13824375/847349). The problem is with the linker optimizations in MSVC throwing out types inaccessible from `main`. – Dmitry Ledentsov Aug 25 '14 at 15:25

1 Answers1

2

Here is a fully working example :

#include <iostream>
#include <map>

namespace detail {
    struct FuncPtrMap {
        friend struct FuncPtrRegisterer;
        using FuncPtr = void(*)();
        static std::map<std::string, FuncPtr> const &getMap() {
            return getWritableMap();
        }

    private:
        // SIOF-proof singleton
        static std::map<std::string, FuncPtr> &getWritableMap() {
            static std::map<std::string, FuncPtr> theMap;
            return theMap;
        }
    };

    // Each static instance will register one function pointer
    struct FuncPtrRegisterer {
        FuncPtrRegisterer(FuncPtrMap::FuncPtr funcPtr, char const *funcName) {
            FuncPtrMap::getWritableMap().emplace(funcName, funcPtr);
        }
    };
}

// Public access to the function pointers map
auto const &gFunctionPointersMap = detail::FuncPtrMap::getMap();

#define DO_CAT(A, B) A##B
#define CAT(A, B) DO_CAT(A, B)

// Registering macro : defines a static registerer
// with a (hopefully) unique name.
#define REGISTER_FUNC(NAME) detail::FuncPtrRegisterer \
    CAT(fpreg, __COUNTER__) (&NAME, #NAME)


//
// Test
//

void myFunc1() {
    std::cout << "func1\n";
}
REGISTER_FUNC(myFunc1);

void myFunc2() {
    std::cout << "func2\n";
}
REGISTER_FUNC(myFunc2);

int main()
{
    for(auto const &kv : gFunctionPointersMap) {
        std::cout << "Calling " << kv.first << " : ";
        kv.second();
    }

    return 0;
}

Prints :

Calling myFunc1 : func1
Calling myFunc2 : func2

Just put a REGISTER_FUNC(func) after each function you wish to register. You only need the declaration of the function, not its definition. Beware that this will not work in header files though.

Afterwards, you can access gFunctionPointersMap at any time from the very start of main :)

Edit : C++03 version here (nothing much changes really).

Quentin
  • 62,093
  • 7
  • 131
  • 191
  • Nice, though I don't think `__COUNTER__` is (technically) in the standard – IdeaHat Aug 25 '14 at 15:47
  • It is not. Were it not implemented on a specific compiler, it can be replaced by `__LINE__` with little difference, but I'll stick with it for semantics. – Quentin Aug 25 '14 at 15:49
  • So each instance of the macro generates a new struct which (in it's constructor) adds the function to the map? What does `fpreg` do? – dshepherd Aug 25 '14 at 16:13
  • 1
    Each instance of the macro generates one instance of the `FuncPtrRegisterer` struct. The instances are named `fpreg0`, `fpreg1`, `fpreg2` and so on thanks to `__COUNTER__`. You can of course change that "fpreg" to whatever you like that will not collide. – Quentin Aug 25 '14 at 16:18