Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Macro alternative for C++ code generation

My settings module has some redundant code:

#include <QSettings>

class MySettings
{
public:
    // param1
    void setParam1(QString param1) { _settings.setValue("param1", param1); }
    string param1() { return _settings.value("param1").toString(); }

    // param2
    void setParam2(int param2) { _settings.setValue("param2", param2); }
    int param2() { _settings.value("param2").toInt(); }

    // param3
    void setParam3(int param3) { _settings.setValue("param3", param3); }
    int param3() { _settings.value("param3").toInt(); }

private:
    QSettings _settings;
}

I managed to reduce the amount of code to write by using a macro. Here is an example for the QString parameter type:

#define INTSETTING(setter, getter) \
    void set##setter(QString getter) { settings.setValue(#getter, getter);} \
    QString getter() {return settings.value(#getter).toString();}

Since I'm using C++, I know that macro usage is bad. I'm looking for a cleaner alternative.

I gave a Qt example (QString) but it is a more general question.

Edit:

The macros make the definition of the above class much simpler:

class MySettings
{
public:
    STRINGSETTING(Param1, param1)
    INTSETTING(Param2, param2)
    INTSETTING(Param3, param3)

    STRINGSETTING(DefaultTitle, defaultTitle)
    INTSETTING(MaxDocCount, maxDocCount)

private:
    QSettings _settings;
}
like image 690
Martin Delille Avatar asked Feb 22 '14 13:02

Martin Delille


People also ask

What is macro replacement in C?

Macro substitution is a mechanism that provides a string substitution. It can be achieved through "#deifne". It is used to replace the first part with the second part of the macro definition, before the execution of the program. The first object may be a function type or an object.

Can we use macro for function in C?

In C, function-like macros are much similar to a function call. In this type of macro, you can define a function with arguments passed into it. TechVidvan Tutorial: Macros with arguments! In the above example, the compiler finds the name of the macro (AREA(a)) and replaces it with the statement (a*a).

What is macro code in C?

The macro in C language is known as the piece of code which can be replaced by the macro value. The macro is defined with the help of #define preprocessor directive and the macro doesn't end with a semicolon(;). Macro is just a name given to certain values or expressions it doesn't point to any memory location.


2 Answers

You can either answer this in a religious fashion, or you can go back to the old principle: if it makes your code more readable, do it.

There are a lot of people who answer this in a religious way, they just hate the preprocessor and everything that's to do with it, and ban its use from their code.

On the other hand, there are people who routinely define macros to do repetitive task, I have done so on several occasions, most frequently just defining a macro for the use within a single function (used much in the way you can define subfunctions in GNU-C).

I think, the way people think about it is quite similar to the way people think about the goto statement: Most deamonize its use, others say it has its positive uses and should not be viewed as evil in itself. You need to decide this for yourself.

like image 98
cmaster - reinstate monica Avatar answered Sep 23 '22 15:09

cmaster - reinstate monica


Here is one way that does not use macros:

class MySettings
{
public:
    template <size_t N>
    void setParam(QString param) { _settings.setValue(names[N], param); }

    template <size_t N, typename T>
    T param() { return _settings.value(names[N]).toString(); }

private:
    QSettings _settings;
    const char* names[3] = { "param1", "param2", "param3" };
}

You change the syntax a little so say e.g. settings.setParam<1>("string") and settings.param<1, string>() but in any case names param1, param2 etc were not so informative.

The only inconvenience is that the caller needs to specify the return type of param() apart from the parameter number. To get rid of this, you can specify all parameter types within MySettings, like this:

class MySettings
{
    using types = std::tuple<string, int, int>;

public:
    template<size_t N>
    void setParam(QString param) { _settings.setValue(names[N], param); }

    template<size_t N>
    typename std::tuple_element<N, types>::type
    param() { return _settings.value(names[N]).toString(); }

private:
    QSettings _settings;
    const char* names[3] = { "param1", "param2", "param3" };
}

You could of course further generalize this class to be used as a base for other settings classes. Within the base, the only things that need to be customized are members types and names.

However, keep in mind that if parameter names are informative indeed unlike your example, e.g. setTitle, setColor etc. then most probably there is no way to avoid macros. In this case, I prefer a macro that generates an entire struct rather than a piece of code within another class, hence probably polluting its scope. So there could be a struct for each individual parameter, generated by a macro given the parameter name. The settings class would then inherit all those individual structs.

EDIT

I "forgot" generalizing toString() in param() (thanks @Joker_vD). One way is this:

    template<size_t N>
    typename std::tuple_element<N, types>::type
    param() {
        using T = typename std::tuple_element<N, types>::type;
        return get_value(type<T>(), _settings.value(names[N]));
    }

where get_value<T>() is a helper function that you need to define and overload for the types supported by QSettings, calling the appropriate conversion member function for each type, for instance

 template<typename V>
 string get_value(type<string>, const V& val) { return val.toString(); }

 template<typename V>
 int get_value(type<int>, const V& val) { return val.toInt(); }

and type is just a helper struct:

 template<typename T>
 struct type { };

If QSettings itself was designed with templates in mind, you wouldn't need this. But you probably wouldn't need a wrapper in the first place.

like image 41
iavr Avatar answered Sep 22 '22 15:09

iavr