I'm trying to represent some meta information in a structured way (i.e. using a class). It's header-only and I need to support c++11 so can't use inline variables. I've come up with a couple of potential solutions but each has its drawbacks. Any suggestions would be much appreciated though just pointing on how to make "alternative B" to compile would be a great solution for me.
SthInfo::fieldA
is used as an argument to a templated Processor<Type>::work(field)
which :
#include <type_traits>
template <typename Container, int ID>
class FieldInfo {
public:
static constexpr int id = ID;
};
template <typename T>
class Processor {
public:
template <typename Container, int ID>
void work(FieldInfo<Container, ID> field) {
static_assert(std::is_same<Container, T>::value, "Given field can't be processed - container type mismatch");
// some business logic using the ID, i.e. accessing `field.id`
int id = field.id;
}
};
struct Sth {/* contains fieldA and fieldB - just as an example */};
// SthInfo holds meta-information about fields in Sth
struct SthInfo {
static constexpr FieldInfo<Sth, 1> fieldA{};
};
int main() {
Processor<Sth> processor;
processor.work(SthInfo::fieldA);
}
This works (compiles and links) fine on Linux and Windows. However, Is there a way to avoid the ID
constant in the template and have it as a field in the FieldInfo
class? Any other improvement ideas?
I've tried changing to the following code but it doesn't link on Linux (but does on Windows...) with undefined reference to SthInfo::fieldA
:
#include <type_traits>
template <typename Container>
class FieldInfo {
public:
const int id;
};
template <typename T>
class Processor {
public:
template <typename Container>
void work(FieldInfo<Container> field) {
static_assert(std::is_same<Container, T>::value, "Given field can't be processed - container type mismatch");
// some business logic using the ID, i.e. accessing `field.id`
int id = field.id;
}
};
struct Sth {/* contains fieldA and fieldB - just as an example */};
// SthInfo holds meta-information about fields in Sth
struct SthInfo {
static constexpr FieldInfo<Sth> fieldA{1};
};
int main() {
Processor<Sth> processor;
processor.work(SthInfo::fieldA);
}
Changing SthInfo::fieldA
to a constexpr function helps but then you have to use ()
when using in the app code...
#include <type_traits>
template <typename Container>
class FieldInfo {
public:
const int id;
};
template <typename T>
class Processor {
public:
template <typename Container>
void work(FieldInfo<Container> field) {
static_assert(std::is_same<Container, T>::value, "Given field can't be processed - container type mismatch");
// some business logic using the ID, i.e. accessing `field.id`
int id = field.id;
}
};
struct Sth {/* contains fieldA and fieldB - just as an example */};
// SthInfo holds meta-information about fields in Sth
struct SthInfo {
static constexpr FieldInfo<Sth> fieldA() { return FieldInfo<Sth>{1}; }
};
int main() {
Processor<Sth> processor;
processor.work(SthInfo::fieldA());
}
Prior to C++17, which introduced inline variables—and made constexpr static member variables implicitly inline—you have to define such variables outside the class if they are odr-used:
const FieldInfo<Sth> SthInfo::fieldA;
Note that this is a non-templated variable and thus must be defined in exactly one source file. Similar definitions of templated variables can appear in headers (using the same compiler/linker support as used for inline variables), so you’ll want to use something like
template<class T>
struct Info {
static constexpr FieldInfo<T> fieldA{1};
};
template<class T> const FieldInfo<T> Info<T>::fieldA;