Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling functions with the same name in a list of namespaces

Tags:

c++

I have several namespaces, each with the same set of function names:

namespace one {
void a();
void b();
void c();
}

namespace two {
void a();
void b();
void c();
}

namespace three {
void a();
void b();
void c();
}

Is there a nice way I can refactor the following code to avoid duplication?

one::a();
two::a();
three::a();

one::b();
two::b();
three::b();

while(true) {
  one::c();
  two::c();
  three::c();
}

For example, in pseudocode, is there a way I can express

for (name: {one, two, three}) {
  name::a();
}

cleanly?

I am also able to rewrite/restructure the function definitions, if there is a more idiomatic way to express the same idea.

like image 883
yoyoy Avatar asked Jan 16 '20 04:01

yoyoy


Video Answer


3 Answers

You can't iterate through namespaces like that, but you could iterate through the different functions by explicitly listing them:

for (auto f : {one::a, two::a, three::a})
{
    f();
}

If you need to do this a lot though, I'd probably just write a function in global scope to call all the others. Or you could write a macro to shorthand the list above:

#define func_list(name) {one::name, two::name, three::name}

for (auto f : func_list(a))
{
    f();
}

It sort of depends on what you need to do in specific cases. However I would just suggest renaming them to be different or making differently-named wrapper functions in global scope.

like image 189
Cruz Jean Avatar answered Oct 22 '22 23:10

Cruz Jean


If I have a full freedom on choosing mechanisms to use and c++14 compatible compiler I'd probably use tag dispatching + argument dependent lookup flavored with generic lambda to customize function call (choose the function to call afterwards):

#include <iostream>

namespace one {
struct Tag { } tag;
void a(Tag) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
void b(Tag) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
void c(Tag) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
}

namespace two {
struct Tag { } tag;
void a(Tag) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
void b(Tag) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
void c(Tag) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
}

namespace three {
struct Tag { } tag;
void a(Tag) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
void b(Tag) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
void c(Tag) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
}

template <class L, class... Ts>
void foreach(L&& l, Ts&& ...ts) {
    int execute[] = { 0, (l(ts), 1)... };
    static_cast<void>(execute);
}

int main() {
    foreach([](auto tag) { a(tag); }, one::tag, two::tag, three::tag);
}

output:

void one::a(one::Tag)
void two::a(two::Tag)
void three::a(three::Tag)

[live demo]

like image 40
W.F. Avatar answered Oct 23 '22 00:10

W.F.


You could turn your namespaces into types.

struct one {
  static void a();
  static void b();
  static void c();
};

struct two {
  static void a();
  static void b();
  static void c();
};

struct three {
  static void a();
  static void b();
  static void c();
};

Then to call all the a functions, then all the b functions, then all the c functions:

template <typename... Namespaces>
void foo() {
  (Namespaces::a(), ...); // expands into one::a(), two::a(), three::a()
  (Namespaces::b(), ...);
  (Namespaces::c(), ...);
}

foo<one, two, three>();
like image 28
Indiana Kernick Avatar answered Oct 22 '22 23:10

Indiana Kernick