tags:

views:

1625

answers:

4

So here's my problem:

struct A
{
    enum A_enum
    {
        E0,
        E1,
        E2
    };
};

struct B
{
    typedef A::A_enum B_enum;
    bool test(B_enum val)
    {
        return (val == E1); // error: "E1" undeclared identifier
    }
};

I specifically do not want to say A::E1. If I try B_enum::E1 I receive a warning that it is nonstandard. Is there a good way to do something like this?

+2  A: 

I reckon that A should be a namespace instead of a struct.

ChrisW
Well A has other stuff too, but maybe it would be better to put it in a namespace.
rlbond
You can also define an enum at global scope ... or, in a base class that's inherited by the classes which use it.
ChrisW
A: 

Why do you even have the test method in struct B? I don't think it makes any sense.

By defining struct A and defining an enum inside it, you are more or less saying that "here's an enum in scope of struct A" and this is the same as saying "if you want to use this enum, you have to refer to the struct A".

Putting the enum inside a namespace will not solve your problem. Because you will have the same scope problem (C4482)

I think you are making things too complicated. What do you think of this?

struct A
{
    enum A_enum
    {
        E0,
        E1,
        E2
    };

    static bool test(A_enum val)
    {
     return (val == E1);
    }
};


int main()
{
    assert(A::test(A::E1));
    return 0;
}

Note that A::test() is static. It should be because you aren't operating on the struct state. And since it's static, you dont need an instance of A to call the method.

Magnus Skog
It's an example. The real case is more complicated. No, I can't do it that way.
rlbond
+1  A: 

Putting enum in global scope is too exposed, putting them in a class can introduced undesired dependency. For enum not tightly link to a class, this is what I use:

#define CLEANENUMS_BEGIN(name) namespace name { typedef enum {
#define CLEANENUMS_END(name) } internal_ ## name ## _e;} typedef name::internal_ ## name ## _e name ## _e;

Then you can use, at global scope:

CLEANENUMS_BEGIN(myEnum)
    horizontal,
    vertical,
CLEANENUMS_END(myEnum)

That is more or less emulating C# way of handling enums scope. The preprocessor will produce this code:

namespace myEnum
{
    enum internal_myEnum_e
    {
        horizontal,
        vertical,
    }
}
typedef internal_myEnum_e myEnum_e;

Then a given enum is referenced as

myEnum_e val = myEnum::horizontal;

Hopefully there's a better way of doing this but so far, that's the only solution I found.

When C++0x will come...whenever this will be...there will be "enum class MyEnum {... };" that works like the C# enum (with the enum values in an own "namespace"). Indeed the syntax was taken from C++/.CLI (the .NET C++) where it is used to emit .NET enums.
rstevens
How do you refer to the elements then?
rlbond
you can say EnumName::Enumerator, and can also declare them in somewhere else using a using-declaration like "using EnumName::Enumerator;" to make the visible without prefixing with EnumName:: . this comes with the new underlying type specification, anyway. for "enum class", the default is int. You can change it with "enum class EnumName : IntegerType { ... };" and the same works of course also for unscoped enums "enum EnumName : int { ... }", where there is no default. With that, the possibility of "forward declaring" comes too: "enum EnumName : int;".
Johannes Schaub - litb
A: 

I've had the same issue in the past, and this is how I've fixed it. I wanted to be able to switch the implementation of a library at compile time. One of the lib was using code like this:

namespace Lib1 
{
  enum LibEnum { One, Two, Three };
  [...]
  void someFunc(LibEnum val);
}

In my code, I wanted to hide the library implementation from the user experience (so a user of my code should never see what lib I'm using internally):

Solution 1:

namespace MyCode 
{
  // Example to avoid copying a function from Lib1 here
  typedef Lib1::someFunc aFunctionImUsing;

  // This doesn't work
  // typedef LibEnum MyEnum; 
  // As such code doesn't compile:
  // aFunctionImUsing(One); // Error unknown identifier One
  // But this did:
  struct Type
  {
     enum MyType { One = Lib1::One, Two = Lib1::Two, Three = Lib1::Three };
  }
  static inline Lib1::LibEnum as(Type::MyType t) { return (Lib1::LibEnum)t; }

  // Now code like this compiles:
  aFunctionImUsing(as(Type::One));
  // This one doesn't:
  // aFunctionImUsing(Type::One); // Can't convert from Type::MyType to Lib1::LibEnum

  [...]
}

Solution 2:

namespace MyCode 
{
  struct Type
  {
     enum MyType { One = Lib1::One, Two = Lib1::Two, Three = Lib1::Three };
  }

  // If you don't care about polluting your namespace with numerous wrapper 
  // you can write this instead of typedef someFunc:
  static inline void myFunc(Type::MyType t) { return Lib1::someFunc((Lib1::LibEnum)t); }

  // This one does:
  myFunc(Type::One); 
  [...]
}

They are 2 issues with the code fragment above. The first issue is that you must copy & paste the enum inside your namespace, (but with a simple regular expression in find&replace, you're done). The second issue is that your user will have to use the "as" method, which means that it's not straightforward, or you have to wrap the method/function using the second solution.

Anyway, as it's not possible to inject an enum across namespace, this solution is the best you can do. Notice that your code user don't even know you're using the Lib1 library in the example code.

X-Ryl669