Well, it's really hard to determine what the real problem you're trying to solve is. BUT, based on the bare minimum example you gave, this is how you might do it with templates:
template <int I>
class SomeClass {
public:
class UniqueData{};
UniqueData data;
static UniqueData static_data;
void doStuff() { doWhatever(data); }
void doStuff2() { doUniqueStuff(data); }
void doUniqueStuff(UniqueData uniqueData);
};
void doCDE() { }
void doABC(SomeClass<1>::UniqueData) { }
void doPQR();
void doMNO(SomeClass<2>::UniqueData) { }
template<>
void::SomeClass<1>::doUniqueStuff(SomeClass<1>::UniqueData data) {
doABC(data);
doCDE();
}
template<>
void::SomeClass<2>::doUniqueStuff(SomeClass<2>::UniqueData data) {
doMNO(data);
doPQR();
}
int main() {
SomeClass<1>().doStuff2();
SomeClass<2>().doStuff2();
}
Here, the template instance has its own unique datatype, and two functions. And these two functions are specialized to call different functions.
EDIT: You specified that you want UniqueData
to have some unique members. If you're willing to be flexible, you can use tuples for that:
#include <functional>
#include <iostream>
#include <tuple>
#include <string>
template<typename T>
void doWhatever(T) { }
// This is now a variadic template. All arguments after id are packed into TUniqueDataMembers
template <int id, typename... TUniqueDataMembers>
class SomeClass {
public:
// UnqiueData has a tuple member (with TUniqueDataMembers). It has one member of each template argument given
class UniqueData {
public:
UniqueData() : data(std::make_tuple(TUniqueDataMembers{}...)) { }
std::tuple<TUniqueDataMembers...> data;
};
UniqueData data;
static UniqueData static_data;
void doStuff() { doWhatever(data); }
void doStuff2() { doUniqueStuff(data); }
void doUniqueStuff(UniqueData uniqueData);
};
// Using a type definition saves some boilerplate, now that we're using multiple template arguments
using SomeClass1 = SomeClass<1, int>;
using SomeClass2 = SomeClass<2, double, std::string>;
void doCDE() { }
void doABC(SomeClass1::UniqueData data) {
// Use std::get<index>(tuple) to access unique data
std::cout << "int: " << std::get<0>(data.data) << "\n";
}
void doPQR() { }
void doMNO(SomeClass2::UniqueData data) {
std::cout << "double: " << std::get<0>(data.data) << "\n";
std::cout << "string: " << std::get<1>(data.data) << "\n";
}
template<>
void SomeClass1::doUniqueStuff(SomeClass1::UniqueData data) {
doABC(data);
doCDE();
}
template<>
void SomeClass2::doUniqueStuff(SomeClass2::UniqueData data) {
doMNO(data);
doPQR();
}
int main() {
SomeClass1 class1 {};
std::get<0>(class1.data.data) = 42;
class1.doStuff2();
SomeClass2 class2 {};
std::get<0>(class2.data.data) = 2.5;
std::get<1>(class2.data.data) = "hello, world!";
class2.doStuff2();
}
EDIT: Regarding the constraint that UniqueData
must be an ENUM with different members.... okay, you got me there. Templates can't do name substitution. Macros are the only thing that can do that. Unfortunately, C++ macros don't offer the ability to recurse like variadic templates can.
BUT, using variadic macro tricks: Overloading Macro on Number of Arguments
You can get an "up-to-N" argument solution that you can expand as you need. Here is the implementation for up to 4 enum members:
#include <functional>
#include <iostream>
#include <tuple>
#include <string>
template<typename T>
void doWhatever(T) { }
// Variadic macro trick for "up-to-N-argument macros"
#define _GEN_ENUM1(_0) enum class UniqueData { _0 };
#define _GEN_ENUM2(_0, _1) enum class UniqueData { _0, _1 };
#define _GEN_ENUM3(_0, _1, _2) enum class UniqueData { _0, _1, _2 };
#define _GEN_ENUM4(_0, _1, _2, _3) enum class UniqueData { _0, _1, _2, _3 };
#define _GET_GEN_ENUM(_0, _1, _2, _3, NAME,...) NAME
#define _GEN_ENUM(...) _GET_GEN_ENUM(__VA_ARGS__, _GEN_ENUM4, _GEN_ENUM3, _GEN_ENUM2, GEN_ENUM1)(__VA_ARGS__)
// Macro to generate class variadic parameters pass to _GEN_ENUM
#define DECL_SOMECLASS(id, ...)\
class SomeClass##id{\
public:\
_GEN_ENUM(__VA_ARGS__)\
UniqueData data;\
static UniqueData static_data;\
void doStuff() { doWhatever(data); }\
void doStuff2() { doUniqueStuff(data); }\
void doUniqueStuff(UniqueData uniqueData);\
};
// Macro to implements SomeClass<N>::doStuff2() with custom function
#define IMPL_SOMECLASS(id, f_do_unique, f_do_not_unique)\
void SomeClass##id::doUniqueStuff(SomeClass##id::UniqueData data) {\
f_do_unique(data);\
f_do_not_unique();\
}
// Actually declare the classes
DECL_SOMECLASS(1, A, B, C)
DECL_SOMECLASS(2, D, E)
void doABC(SomeClass1::UniqueData data) {
std::cout << "A: " << (int)SomeClass1::UniqueData::A << "\n";
std::cout << "B: " << (int)SomeClass1::UniqueData::B << "\n";
std::cout << "C: " << (int)SomeClass1::UniqueData::C << "\n";
std::cout << "Data: " << (int)data << "\n";
}
void doCDE() { }
void doMNO(SomeClass2::UniqueData data) {
std::cout << "D: " << (int)SomeClass2::UniqueData::D << "\n";
std::cout << "E: " << (int)SomeClass2::UniqueData::E << "\n";
std::cout << "Data: " << (int)data << "\n";
}
void doPQR() { }
IMPL_SOMECLASS(1, doABC, doCDE)
IMPL_SOMECLASS(2, doMNO, doPQR)
int main() {
SomeClass1 class1 {};
class1.data = SomeClass1::UniqueData::C;
class1.doStuff2();
SomeClass2 class2 {};
class2.data = SomeClass2::UniqueData::E;
class2.doStuff2();
}
NOTE: by using macros instead of templates, you lose all type safety for the arguments of DECL_SOMECLASS
and IMPL_SOMECLASS
. So you can pass "lol"
as the id
and witness horrifying error messages. Here be dragons.