davidbak
davidbak

Reputation: 5999

From enum type declared in a class/struct get to a peer static method also declared in that class/struct?

Briefly: In a template that takes an enum type as parameter (not a scoped enum, a regular old pre-C++11 enum) I need to call a method, declared in the same struct as the enum, which takes an enum value as its parameter. Is there a way to do this?

In full: Suppose I'm "self-scoping" unscoped enums as follows:

struct W {
   enum E { A, B, C };

   static string foo(enum E e);
};

Now suppose I have a bunch of these struct declarations following this pattern - each has an enum though the enum type's name is different and each has a static method foo taking one argument which is of that enum type.

Now I want to create a template that given a bunch of one of these enum types wants to transform each one according to its own foo():

template <typename E>
vector<string> xform(const vector<E> es) {
  vector<string> ss;
  for (E e : es) {
    ss.push_back(foo(e));
  }
  return ss;
}

Now I instantiate xform:

...
    vector<enum W::A> as{W::A, W::C};
    auto Wx = xform(as);
...

and of course I get a compiler error because the compiler can't find the right foo to call:

prog.cc: In instantiation of 'std::vector<std::__cxx11::basic_string<char> > xform(std::vector<E>) [with E = W::A]':
prog.cc:34:24:   required from here
prog.cc:24:21: error: 'foo' was not declared in this scope
   24 |     ss.push_back(foo(e));
      |                  ~~~^~~

(This is all here on wandbox.)

So I need to get from the enum type to its peer method. Can I do that - how? (Since the peer method's name is always the same that part's easy - I don't know how to get from the enum type to its enclosing type.)

(Obviously I can solve this if the enum type's name in each struct is the same, by using the struct's name as the template argument. But in my use case the enums all have different names.)

Resolution (at this point): D-RAJ's answer though very simple doesn't work in this particular case because of the function foo is not dependent on the type parameter in the template function xform, thus the rules related to lookup and ADL and a non-dependent name in a template mean that ADL can not be used if you try to implicitly instantiate xform in code in some other namespace. (I'm not sure I understand why, but that's the fact.) The error is that the name cannot be found at the point of template instantiation, only at the point of declaration (and only then via ADL). I guess you could get around that with explicit instantiations ... but ...

dxiv's answer of using traits works great, is not onorous, and it can be done without modifying the existing wrapped-enums at all.

Upvotes: 1

Views: 234

Answers (2)

dxiv
dxiv

Reputation: 17638

I don't know that it's possible as stated, and don't think it is.

A relatively cheap alternative could be to maintain the association between names by hand. In the sample code below, this is done by specializations of a helper template WX<> (tried here).

#include <string>
#include <vector>
using std::string;
using std::vector;

struct W1 {
  enum E1 { A, B, C };
  static string foo(enum E1 e);
};

struct W2 {
  enum E2 { A, B, C };
  static string foo(enum E2 e);
};

template<typename T> struct WX;
template<> struct WX<W1::E1> { using W = W1; };
template<> struct WX<W2::E2> { using W = W2; };

template<typename E> vector<string> xform(const vector<E> es) {
  vector<string> ss;
  for (E e : es) {
    ss.push_back(WX<E>::W::foo(e));
  }
  return ss;
}

void bar()
{
  vector<enum W1::E1> a1s { W1::A, W1::C };
  auto w1x = xform(a1s);
  vector<enum W2::E2> a2s { W2::A, W2::C };
  auto w2x = xform(a2s);
}

Upvotes: 1

D-RAJ
D-RAJ

Reputation: 3372

In simple, No, you cant do it like that. There are 2 reasons for this,

  1. vector<enum W::A> and template <typename E> vector<string> xform(const vector<E> es) doesn't let you find which struct contains the function foo(). This is because the std::vector::_Ty = enum W::A which is basically an enum type. How can you find a struct from an enum type?
  2. When you define the static foo() function in a struct or class, the function is in the scope of the struct/ class (its the same as namespace W { string foo(...) { ... } }). This is the reason for this error: prog.cc:24:21: error: 'foo' was not declared in this scope.

How to resolve this issue?
The only reasonable option that comes to my mind is to put the foo() function in the global scope and provide overrides to it. It would look something like this.

struct W1 {
    enum A { A, B, C };
};

string foo(enum W1::A a) { return std::array{ "A","B","C" } [a] ; }

struct W2 {
    enum B { X, Y, Z, };
};

string foo(enum W2::B b) { return std::array{ "X", "Y", "Z" } [b] ; }

Now the xform() function can resolve which foo() to be used at compile time without any scope issues.

Upvotes: 4

Related Questions