Parametric function in namespaces (C++)

103 views Asked by At

I need to define 3 functions that have the same goal, but whose behaviours changes slightly based on 3 sets of constant values; in other words, i could simply write a function that does that in all 3 cases by taking those values as inputs. But since there really many constants (and only 3 different sets of those) i'd definitely avoid such a long function declaration. Furthermore, i'll need those sets of constants in other files for related computations.

I was thinking about using namespaces, but i couldn't find anything that suited what i wanted to achieve. Just to make things more comprehensible, here is an example of what i'd desire (but obviously doesn't compile):

int parametric_function() {
    return a_constant + 1; //'a_constant' isn't defined yet
}

namespace first_behaviour {
    const int a_constant = 10;

    //make the function use the variable 'a_constant' defined here in some way
    int (*f)() = parametric_function;
}

namespace second_behaviour {
    const int a_constant = 20;

    //make the function use the variable 'a_constant' defined here in some way
    int (*f)() = parametric_function;
}

As you can see, i'd only need to write my parametric function once, and i can use the namespace to get the right function and the associated set of constants. Do you have any suggestions on what i could try doing?

4

There are 4 answers

0
HolyBlackCat On

If you have only one variable, you can pass it directly as a template parameter, as suggested in this answer.

But if you have more than one, you can wrap them in a struct:

#include <iostream>

struct FirstBehavior
{
    static constexpr int a_constant = 10;
};
struct SecondBehavior
{
    static constexpr int a_constant = 10;
};

template <typename T>
int ParametricFunction()
{
    return T::a_constant + 1;
}

int main()
{
    std::cout << ParametricFunction<FirstBehavior>() << '\n'; // 1
}
0
Lala5th On

Possibly you could do with templates. You could:

template <int CONST_VAL>
int par_func();

template<>
int par_func<10>(){ return 4; }

template<>
int par_func<20>(){ return 1; }

template<>
int par_func<30>(){ return 9; }

You could then alias these names to some other function if you want, or you can leave them like this. This also ensures that only the specialisations can be used.

You can also do your example like:

template <int CONST_VAL>
int par_func(){
    return CONST_VAL + 1;
}

You can then put this in an implementation file and explicilty instantiate only the ones you use, like:

template int par_func<10>();

You can use this the same way with your namespace model like:

namespace func1 {
    int(* func)() = &par_func<10>;
}

namespace func2 {
    int(* func)() = &par_func<20>;
}
0
Goswin von Brederlow On

In c++ you have templates:

template <int a_constant>
int parametric_function() {
    return a_constant + 1;
}

namespace first_behaviour {
    auto f = parametric_function<10>;
}
0
Eljay On

Using HolyBlackCat's suggestion of a struct and a template, here would be one approach.

The struct is just a wrapper to hold the variable. In this example, I made it a stateful variable (non-const), static to the struct wrapper. It has the requirement to be the expected name by the parameteric_function.

I thought making the example use a non-const variable might be more generally applicable for other types, such as std::string or std::vector or whatever you may need.

The extern int (*f)(); was just to squelch a compiler warning.

#include <iostream>

using std::cout;

template <typename T>
int parametric_function() {
    ++T::a_variable;
    return T::a_variable;
}

namespace first_behaviour {
struct VarHolder {
    static inline int a_variable = 10;
};
extern int (*f)();
int (*f)() = &parametric_function<VarHolder>;
} // first_behaviour

namespace second_behaviour {
struct OtherVarHolder {
    static inline int a_variable = 20;
};
extern int (*f)();
int (*f)() = &parametric_function<OtherVarHolder>;
} // second_behaviour

int main() {
    int x = first_behaviour::f();
    int y = second_behaviour::f();
    cout << x << " " << y << "\n";
}