Jump to content
  • Advertisement
Sign in to follow this  
Decrius

[C++] Appending enums / generalising enums

This topic is 3244 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, I have one Enum representing the different events that can be emitted, it's a list of possible events. So each event is a number with a compile-time name. Now I have multiple header files that all introduce new events and thus would like to give those a unique event number. I can do that with macro's of course, but that does require different enum's per time I add new events. Now, I also have classes (my signal and slot system) which depends on an enum as signal type. ATM I have templated that type. Is it OK to generalize these enums and get rid of the template type, then to replace it with unsigned int's? All events are bigger then 0, and very unlikely to exceed 4 Gi. Does anyone have a nice system to append new unique ID's to an enum? My enums are all inside their own namespace, so defining a new enum per time you add new ID's is no problem. Thanks!

Share this post


Link to post
Share on other sites
Advertisement
Hi
I don't know about your particular situation, of course, so the following may or may not apply to you.

Why do you care about unique IDs *AND* want to have separate enums? It does not make much sense to me. I guess you should probably have one big enum. Make event handlers to care only about, well, the events they care about. (and get rid of the templated signal/slot system which might be overkill imho)

Share this post


Link to post
Share on other sites
Yes, I have that setup now. The problem is that I cannot add more events from another library:

Suppose you have 2 source files and 3 header files:

base.hpp
enum Enum
{
Alpha,
Beta,
Last
};

#define ENUM_LAST Last


header1.hpp
#include "base.hpp"

enum Enum1
{
Gamma = ENUM_LAST,
Delta,
Last1
};

#undef ENUM_LAST
#define ENUM_LAST Last1


header2.hpp
#include "base.hpp"

enum Enum2
{
Epsilon = ENUM_LAST,
Xi,
Last2
};

#undef ENUM_LAST
#define ENUM_LAST Last2


Now, when compiling the 2 source files which include the latter 2 header files, Epsilon == Gamma, and Delta == Xi. This is not what I want, as it will conflict elsewhere in the code.

Share this post


Link to post
Share on other sites
I can imagine a system where you have a lazy global object that generates unique integer ID's on demand. If this object was available to all files that needed to define event ID's, you could simply assign its ID's to const ints, and it would all be taken care of at global init time.
class IDGen{
unsigned curr_id;
//you could add machinery for freeing and re-using id's
public:
IDGen():curr_id(0){}
unsigned GetId(){return curr_id++;}
};

//lazy global, to make sure the obj is initialized
//sorta like a singleton, but less gross
IDGen& idgen(){
static IDGen _idgen;
return _idgen;
}

//now in all the files that need id's...
const unsigned EVENT_FOO = idgen().GetId();

That was even easier than I thought, but perhaps it's overkill. It is, however, a re-usable solution. The reason it came so quickly to mind is that I've been considering something similar for game object ID's.

Share this post


Link to post
Share on other sites
Quote:
Original post by theOcelot
I can imagine a system where you have a lazy global object that generates unique integer ID's on demand. If this object was available to all files that needed to define event ID's, you could simply assign its ID's to const ints, and it would all be taken care of at global init time.
*** Source Snippet Removed ***
That was even easier than I thought, but perhaps it's overkill. It is, however, a re-usable solution. The reason it came so quickly to mind is that I've been considering something similar for game object ID's.


Ahem ;)

Effectively, the standard allows that for every single run, all those id's are perfectly allowed to be different from the previous run. Even forks and threads of the running program might have different id's, if not shared.

Share this post


Link to post
Share on other sites
I know. That's fine, as long as they are unique for the duration of the program. I guess you might have problems with threading, if the IDGen object wasn't synchronized. I admit that I don't usually bother my head with threading and related issues. But if Decrius doesn't have to either, my idea might still work.

Share this post


Link to post
Share on other sites
Yes, it's a nice idea.

What if you have a global object of this running in the main thread, and any other thread asks a new ID from the main thread, there shouldn't be an issue, should there? Just make sure the counter is only ever once in memory, and only increases.

This does move the problem to a run-time issue, I would've preferred a compile-time solution, but I'm not sure if that's really possible. Except if I make a major list of all events at the top of the hierarchy of the library.

Share this post


Link to post
Share on other sites
Quote:
Original post by theOcelot
I know. That's fine, as long as they are unique for the duration of the program.


It's a nice idea, but it doesn't work. The values are not consistent.


// idgen.h
#pragma once

class IDGen
{
unsigned curr_id;
//you could add machinery for freeing and re-using id's
public:

IDGen():curr_id(0)
{}

unsigned GetId()
{
return curr_id++;
}
};

//lazy global, to make sure the obj is initialized
//sorta like a singleton, but less gross
IDGen& idgen();

// idgen.cpp
#include "idgen.h"

//sorta like a singleton, but less gross
IDGen& idgen()
{
static IDGen _idgen;
return _idgen;
}

// foo.h
#pragma once
#include "idgen.h"
const unsigned FOO = idgen().GetId();
void foo();

// foo.cpp
#include <iostream>
#include "foo.h"
#include "bar.h"

void foo()
{
std::cerr << "FOO in foo.cpp : " << FOO << "\n";
std::cerr << "BAR in foo.cpp : " << BAR << "\n";
}

// bar.h
#pragma once
#include "idgen.h"
const unsigned BAR = idgen().GetId();
void bar();

// bar.cpp
#include <iostream>
#include "foo.h"
#include "bar.h"

void bar()
{
std::cerr << "FOO in bar.cpp : " << FOO << "\n";
std::cerr << "BAR in bar.cpp : " << BAR << "\n";
}

// main.cpp
#include "foo.h"
#include "bar.h"

int main(int argc, char* argv[])
{
bar();
foo();
return 0;
}



Output :

FOO in bar.cpp : 0
BAR in bar.cpp : 1
FOO in foo.cpp : 2
BAR in foo.cpp : 3
Press any key to continue . . .

Share this post


Link to post
Share on other sites
Quote:
Original post by _moagstar_
Quote:
Original post by theOcelot
I know. That's fine, as long as they are unique for the duration of the program.


It's a nice idea, but it doesn't work. The values are not consistent.

*** Source Snippet Removed ***

Output :

FOO in bar.cpp : 0
BAR in bar.cpp : 1
FOO in foo.cpp : 2
BAR in foo.cpp : 3
Press any key to continue . . .


You could make the declarations of the IDs in the headers extern, and define them (and assign a value from idgen()) in the corresponding source files. Then the values will be unique (but still only for one given run of the program!)

Share this post


Link to post
Share on other sites
Well obviously, you include foo.h and bar.h twice each, with a resulting four calls to GetID. My system doesn't change the fact that if you want to access variables from different .cpp's, you have to extern the variables, not declare them in a header file.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!