I recently started using tuples instead of plain class members because I find it convenient to work with them. So my code looks something like this:
class TestClass final {
public:
TestClass() = default;
~TestClass() = default;
public:
template<int M>
auto get()->decltype(std::get<M>(m_private_members)) const {
return std::get<M>(m_private_members);
}
enum PrivateIdx {
count,
use_stuff,
name
};
private:
std::tuple<int, bool, std::string> m_private_members{1, true, "bla"};
};
So this can be used now like:
std::cout << t.get<TestClass::name>()> << std::endl;
This work also fine - the only thing is, adding members can be quite error-prone. One can easily get the access enums wrong by mixing up the order or forgetting a member. I was thinking of a macro style thing like:
PUBLIC_MEMBERS(
MEMBER(int count),
MEMBER(std::string name)
);
This would expand to the tuple and enum code. Problem is, I don't think this can be solved by a macro because it's two different data structures it would have to expand to, right? Also, I must admit, I've never looked into complicated macros.
I was also thinking of a template for solving this, but I could also not come up with a viable solution because enums cannot be generated by a template.
Interesting problem. I'm curious why you'd like to do this. This is something I came up with. Good news: no macros!
The main problem, I think, is that you want to declare identifiers to access members. This cannot be solved with templates, so you have to either a) use macros, or b) somehow declare those identifiers without directly. Instead of using constants/enumerations, I tried to use type names to identify member in get.
I'll start with an example of use:
class User
{
public:
enum class AccessLevel
{
ReadOnly,
ReadWrite,
Admin
};
struct Name : MemberId<std::string> {};
struct Id : MemberId<unsigned> {};
struct Access : MemberId<AccessLevel> {};
template<typename MemberType>
auto& get() { return PrivMembers::getFromTuple<MemberType>(m_members); }
template<typename MemberType>
const auto& get() const { return PrivMembers::getFromTuple<MemberType>(m_members); }
private:
using PrivMembers = MembersList<Name, Id, Access>;
PrivMembers::Tuple m_members;
};
int main()
{
User user;
user.get<User::Name>() = "John Smith";
user.get<User::Id>() = 1;
user.get<User::Access>() = User::AccessLevel::ReadWrite;
return 0;
}
Name, Id and Access are used for identifying elements of m_members tuple. These structures don't have any members themselves. PrivMembers::Tuple is alias for std::tuple<std::string, unsigned, AccessLevel>:
template<typename Type_>
struct MemberId { using Type = Type_; };
template<typename... Types>
struct MembersList
{
using Tuple = std::tuple<typename Types::Type...>;
template<typename T>
static auto& getFromTuple(Tuple& tp) { return std::get<detail::IndexOf<T, Types...>::value>(tp); }
template<typename T>
static const auto& getFromTuple(const Tuple& tp) { return std::get<detail::IndexOf<T, Types...>::value>(tp); }
};
First thing: Tuple alias. I think its self explanatory what happens. Then, there are to overloads for getFromTuple, which is used by User class.
When using MemberId-derived types instead of constants for accessing elements of the tuple, I need to find index of corresponding to given member Id. That what happens in getFromTuple. There's a helper class which does searching:
namespace detail
{
template<typename Needle, typename HaystackHead, typename... Haystack>
struct IndexOf { static constexpr std::size_t value = IndexOf<Needle, Haystack...>::value + 1; };
template<typename Needle, typename... Haystack>
struct IndexOf<Needle, Needle, Haystack...> { static constexpr std::size_t value = 0; };
}
All of this solves the problem of having to maintain indices for each members, as in your original solution. Syntax for declaring member Ids (struct Name : MemberId<std::string> {};) might be bit annoying, but I cannot think about more compact solution.
All of this works with C++14. If you can live with trailing return type for User::get, then you could compile it as C++11.
Here's full code.
Like I said in comment macros are pain to debug. One who does not see how to write some should think twice if to use these at all. OTOH these are relatively straightforward to write when one gets the logic of those.
Note that given is just one way to do it, like with everything there are several.
So macros are like that:
#define GET_NAME(NAME,TYPE,VALUE) NAME
#define GET_TYPE(NAME,TYPE,VALUE) TYPE
#define GET_VALUE(NAME,TYPE,VALUE) VALUE
#define DECLARE_ENUM(PRIVATES) \
enum PrivateIdx { \
PRIVATES(GET_NAME) \
};
#define DECLARE_TUPLE(PRIVATES) \
std::tuple<PRIVATES(GET_TYPE)> m_private_members{PRIVATES(GET_VALUE)};
#define DECLARE_IN_ONE_GO(PRIVATES) \
public: \
DECLARE_ENUM(PRIVATES) \
private: \
DECLARE_TUPLE(PRIVATES)
And usage is like that:
#include <iostream>
#include <tuple>
#include "enum_tuple_macros.h"
class TestClass final {
public:
TestClass() = default;
~TestClass() = default;
#define PRIVATES(MEMBER) \
MEMBER(count,int,1), \
MEMBER(use_stuff,bool,true), \
MEMBER(name,std::string,"bla")
DECLARE_IN_ONE_GO(PRIVATES)
// note that the get can be also generated by DECLARE_IN_ONE_GO
public:
template<int M>
auto get() const -> decltype(std::get<M>(m_private_members)) {
return std::get<M>(m_private_members);
}
};
int main()
{
TestClass t;
std::cout << t.get<TestClass::name>() << " in one go" << std::endl;
}
Seems to work on gcc 8.1.0 i tried.
In the meantime I came up with something using var args...
taken from
[https://stackoverflow.com/questions/16374776/macro-overloading][1]
#define EXPAND(X) X
#define __NARG__(...) EXPAND(__NARG_I_(__VA_ARGS__,__RSEQ_N()))
#define __NARG_I_(...) EXPAND(__ARG_N(__VA_ARGS__))
#define __ARG_N( \
_1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
_41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
_61,_62,_63,N,...) N
#define __RSEQ_N() \
63,62,61,60, \
59,58,57,56,55,54,53,52,51,50, \
49,48,47,46,45,44,43,42,41,40, \
39,38,37,36,35,34,33,32,31,30, \
29,28,27,26,25,24,23,22,21,20, \
19,18,17,16,15,14,13,12,11,10, \
9,8,7,6,5,4,3,2,1,0
// general definition for any function name
#define _VFUNC_(name, n) name##n
#define _VFUNC(name, n) _VFUNC_(name, n)
#define VFUNC(func, ...) EXPAND(_VFUNC(func, EXPAND( __NARG__(__VA_ARGS__))) (__VA_ARGS__))
#define MEMBER_LIST(...) EXPAND(VFUNC(MEMBER_LIST, __VA_ARGS__))
#define MEMBER_LIST3(mem_type1, mem_name1, default_value1)\
\
enum PrivateIdx { \
mem_name1 \
}; \
\
std::tuple<mem_type1> m_private_members{default_value1}
#define MEMBER_LIST6( mem_type0, mem_name0, default_value0,\
mem_type1, mem_name1, default_value1)\
\
enum PrivateIdx { \
mem_name0, \
mem_name1 \
}; \
\
std::tuple< mem_type0, \
mem_type1 > m_private_members{ default_value0, \
default_value1}
..and so on
Works, but imho still not elegant enough. I think I got pointed in the right direction.
Related
struct First{
First(){
printf("first");
}
}First;
int main()
{
print("second");
return 0;
}
I know I can control the code that is executed first through the structure.
So I want to use the following code to make it simple through macro.
#define FIRST_INVOKE(NAME,FUNCTION) \
struct NAME \
{ \
NAME() \
{ \
FUNCTION(); \
} \
} NAME;
namespace Foo
{
namespace Bar
{
void FirstFunction()
{
LOGD(LVID, "First");
}
}
}
FIRST_INVOKE(UniqueStructName, Foo::Bar::FirstFunction);
When creating a macro, you must specify the name of the structure. (UniqueStructName)
I want to set this to a name that does not overlap globally automatically.
Namespace and class static functions cannot be included in structure names because :: is required.
Please let me know if there is a good way (I am using Xcode.)
You can automatically generate names based on the line number and use anonymous namespace to avoid multiple definitions across files. E.g, using the following macro:
#define CONCAT_(A,B) A##B
#define CONCAT(A,B) CONCAT_(A,B)
#define NONAME() CONCAT(noname_, __LINE__)
#define FIRST_INVOKE(FUNCTION) \
namespace { \
struct NONAME() \
{ \
NONAME()() \
{ \
FUNCTION(); \
} \
} NONAME(); \
}
This will expand to a noname_X struct, where X is the line-number. This works as long as you do not repeat the macro on the same line.
I'm attempting to generate some code with preprocessor.
In some class, I need to define multiple variable member, their corresponding setters and a map containing reference on each declared variable.
To illustrate my need, you can find the following code example of what I want to achieve.
In this example, I only declare two variable but in the real case more variable should be declared in different class:
#include <boost/preprocessor.hpp>
#include <iostream>
#include <string>
#include <unordered_map>
#include <unordered_set>
class typeBase {
};
template <typename T>
class typeBase_: public typeBase {
private:
T value_;
public:
typeBase_() { }
typeBase_(const T& v): value_(v) { }
};
using typeInt = typeBase_<int>;
using typeStr = typeBase_<std::string>;
std::unordered_set<std::string> changed_list_;
//////////////////////////////////////////////////////////
// Here use generation to generate the bellow code
//////////////////////////////////////////////////////////
typeInt mInt_;
typeStr mStr_;
std::unordered_map<std::string, typeBase&> properties_ = {
{"mInt", mInt_},
{"mStr", mStr_}
};
void set_mInt(const typeInt& mInt) {
mInt_ = mInt;
changed_list_.insert("mInt");
}
void set_mStr(const typeStr& mStr) {
mStr_ = mStr;
changed_list_.insert("mStr");
}
/////////////////////////////////////////////
/////////////////////////////////////////////
int main()
{
set_mInt(2);
set_mStr(std::string("test"));
}
At the time I first tried with Boost preprocessing library and I am stuck for the moment at creating the map containing reference to each variable member:
#define MODEL_DECLARE(...) \
std::unordered_map<std::string, typeBase&> properties = { \
MODEL_GENERATE_MAP_ITEMS(BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
};
#define MODEL_GENERATE_MAP_ITEMS(Args) \
BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(MODEL_GENERATE_MAP_ITEM, %%, Args))
#define MODEL_GENERATE_MAP_ITEM(s, Unused, Arg) \
{(MODEL_STRINGIFY(BOOST_PP_TUPLE_ELEM(2, 0, Arg)), BOOST_PP_TUPLE_ELEM(2, 0, Arg))}
#define MODEL_STRINGIFY_(V) #V
#define MODEL_STRINGIFY(V) MODEL_STRINGIFY_(V)
#define MODEL_MAKE_ITEM(s, Unused, Arg) \
{BOOST_PP_TUPLE_ELEM(2, 0, Arg) BOOST_PP_TUPLE_ELEM(2, 1, Arg)}
// Generate model with this line
MODEL_DECLARE((mInt, typeInt), (mStr, typeStr))
With this code I produce this preprocessing line:
std::unordered_map<std::string, typeBase&> properties = { {("mInt", mInt)}, {("mStr", mStr)} };
As you can see, I have parenthesis which need to be removed, which I barrely failed to to.
Do you know a better solution to achieve what I need, or how can I FIX my code to successfully generate the needed code?
Regards
EDIT1:
I started to implement the #parktomatomi solution and I also tryed to add code to declare variable and setters:
#include <boost/preprocessor.hpp>
#include <iostream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <cassert>
class typeBase {
};
template <typename T>
class typeBase_: public typeBase {
private:
T value_;
public:
typeBase_() { }
typeBase_(const T& v): value_(v) { }
};
using typeInt = typeBase_<int>;
using typeStr = typeBase_<std::string>;
std::unordered_set<std::string> changed_list_;
//////////////////////////////////////////////////////////
// Here use generation to generate the bellow code
//////////////////////////////////////////////////////////
template <typename... Ts>
std::unordered_map<std::string, typeBase*> build_properties(Ts&&... args) {
return std::unordered_map<std::string, typeBase*> { { args.first, args.second }... };
}
// Macro used to generate properties map
#define MODEL_GENERATE_MAP_ITEM(Name, Type) std::make_pair( #Name, &Name##_ )
#define MODEL_UNWRAP_MAP_ITEM(Unused1, Unused2, Arg) MODEL_GENERATE_MAP_ITEM Arg
#define MODEL_GENERATE_MAP_ITEMS(Args) BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(MODEL_UNWRAP_MAP_ITEM,,Args))
// Macro used to declare vars and setters
#define MODEL_GENERATE_VAR(Name, Type) Type Name##_; \
void set_##Name(const Type& Name) { \
Name##_ = Name; \
changed_list_.insert(#Name); \
};
#define MODEL_UNWRAP_VAR(Unused1, Unused2, Arg) MODEL_GENERATE_VAR Arg
#define MODEL_GENERATE_VARS(Args) BOOST_PP_SEQ_TRANSFORM(MODEL_UNWRAP_VAR,,Args)
// Macro to generate model
#define MODEL_DECLARE(...) \
std::unordered_map<std::string, typeBase*> properties_ = build_properties( \
MODEL_GENERATE_MAP_ITEMS(BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
); \
MODEL_GENERATE_VARS(BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))
// Generate model
MODEL_DECLARE((mInt, typeInt), (mStr, typeStr))
int main() {
assert(properties_.size() == 2);
assert(properties_["mInt"] == &mInt_);
assert(properties_["mStr"] == &mStr_);
}
However this do not compile because the preprocessing generation add parenthesis around the declaration:
(typeInt mInt_; void set_mInt(const typeInt& mInt) { mInt_ = mInt; changed_list_.insert("mInt"); };) (typeStr mStr_; void set_mStr(const typeStr& mStr) { mStr_ = mStr; changed_list_.insert("mStr"); };)
How to remove this paranthesis?
I ran into two issues trying to get this to compile:
You can't have a container of references
Macros and braces don't mix well
To fix #1, use a pointer instead:
std::unordered_map<std::string, typeBase*>
To fix #2, use a helper function to initialize the map:
template <typename... Ts>
std::unordered_map<std::string, typeBase*> build_properties(Ts&&... args) {
return std::unordered_map<std::string, typeBase*> { { args.first, args.second }... };
}
Then the goal becomes to generate this with macros:
build_properties(
std::make_pair("mInt", &mInt_),
std::make_pair("mStr", &mStr_)
)
Which is a bit easier and compiles successfully:
#define MODEL_GENERATE_MAP_ITEM(Name, Type) std::make_pair( #Name, &Name##_ )
#define MODEL_UNWRAP_MAP_ITEM(Unused1, Unused2, Arg) MODEL_GENERATE_MAP_ITEM Arg
#define MODEL_GENERATE_MAP_ITEMS(Args) \
BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(MODEL_UNWRAP_MAP_ITEM,,Args))
#define MODEL_DECLARE(...) \
std::unordered_map<std::string, typeBase*> properties = build_properties( \
MODEL_GENERATE_MAP_ITEMS(BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
);
// Generate model with this line
MODEL_DECLARE((mInt, typeInt), (mStr, typeStr))
More directly related to your question, the BOOST_PP_VARIADIC_TO_SEQ macro adds a pair of parenthesis around your arguments:
(mInt, typeInt), (mStr, typeStr) ==> ((mInt, typeInt)) ((mStr, typeStr))
So when BOOST_PP_SEQ_TRANSFORM generates its macros, the argument generated for each transformation macro has parenthesis around it, e.g. (mInt, typeInt). To get rid of those parenthesis, I added this macro:
MODEL_UNWRAP_MAP_ITEM(Unused1, Unused2, Arg) MODEL_GENERATE_MAP_ITEM Arg
which when Arg is replaced:
MODEL_GENERATE_MAP_ITEM (mInt, typeInt)
which gets transformed one last time to:
std::make_pair( "mInt", mInt_ )
Demo: https://godbolt.org/z/5fyo3N
Update
To get the updated code working, I had to make 3 changes:
Instead of BOOST_PP_SEQ_TRANSFORM, use BOOST_PP_SEQ_FOR_EACH. The former does a map on each item and turns it back into a SEQ:
BOOST_PP_SEQ_TRANSFORM(ADD_1, , (a)(b)) ==> (a+1)(b+1)
While BOOST_PP_SEQ_FOR_EACH does a mapping operation, but does not add the parenthesis back:
BOOST_PP_SEQ_TRANSFORM(ADD_1, , (a)(b)) ==> a+1 b+1
Since you've got that set_mInt function, you've got to add an assignment operator to typeBase_<T>:
typeBase_& operator =(const typeBase_& that) { value_ = that.value_; return *this; }
MODEL_GENERATE_VARS needs to be before the map declaration so mInt_ and friends are visible
All together:
#define MODEL_GENERATE_MAP_ITEM(Name, Type) std::make_pair( #Name, &Name##_ )
#define MODEL_UNWRAP_MAP_ITEM(Unused1, Unused2, Arg) MODEL_GENERATE_MAP_ITEM Arg
#define MODEL_GENERATE_MAP_ITEMS(Args) BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(MODEL_UNWRAP_MAP_ITEM,,Args))
// Macro used to declare vars and setters
#define MODEL_GENERATE_VAR(Name, Type) Type Name##_; \
void set_##Name(const Type& Name) { \
Name##_ = Name; \
changed_list_.insert(#Name); \
};
#define MODEL_UNWRAP_VAR(Unused1, Unused2, Arg) MODEL_GENERATE_VAR Arg
#define MODEL_GENERATE_VARS(Args) BOOST_PP_SEQ_FOR_EACH(MODEL_UNWRAP_VAR,,Args)
// Macro to generate model
#define MODEL_DECLARE(...) \
MODEL_GENERATE_VARS(BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
std::unordered_map<std::string, typeBase*> properties_ = build_properties( \
MODEL_GENERATE_MAP_ITEMS(BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
);
// Generate model
MODEL_DECLARE((mInt, typeInt), (mStr, typeStr))
Demo: https://godbolt.org/z/bDDe94
Let's say I have some unspecified type called variant, as well as two functions allowing to convert to/from this type, with the following signature:
struct converter
{
template<typename T>
static variant to(const T&);
template<typename T>
static T from(const variant&);
};
Now, what I'd like to do is create wrappers for arbitrary C++ functions as in the following example:
SomeObject f_unwrapped(const std::string& s, int* x)
{
//... do something with the inputs...
return SomeObject();
}
extern "C" variant f(variant s, variant x)
{
return converter::to<SomeObject>(f_unwrapped(converter::from<std::string>(s), converter::from<int*>(x)));
}
Ideally I'd want the wrapper to be a one-line declaration or macro that would take only the f_unwrapped function and the name f as inputs.
I've tried to wrap the function into a function object, then do the bureaucratic work using variadic templates. While this does work, I don't know how to make the resulting function extern "C".
What is the most idiomatic way of achieving this goal?
If we use the EVAL, helper, Conditional, and map macros from the first two code blocks here.
The map will need to be made more general for our needs.
#define MM1() MM_CALL1
#define MM_NEXT1(Macro,a,...) \
IS_DONE(a)( \
EAT \
, \
OBSTRUCT(COMMA)() OBSTRUCT(MM1)() \
) \
(Macro,a,__VA_ARGS__)
#define MM_CALL1(Macro,a,...) \
Macro(a) \
MM_NEXT1(Macro,__VA_ARGS__)
#define MacroMap1(Macro,...) MM_CALL1(Macro,__VA_ARGS__,DONE)
#define MM2() MM_CALL2
#define MM_NEXT2(Macro,a,...) \
IS_DONE(a)( \
EAT \
, \
OBSTRUCT(COMMA)() OBSTRUCT(MM2)() \
) \
(Macro,a,__VA_ARGS__)
#define MM_CALL2(Macro,a,b,...) \
Macro(a,b) \
MM_NEXT2(Macro,__VA_ARGS__)
#define MacroMap2(Macro,...) MM_CALL2(Macro,__VA_ARGS__,DONE)
We will also want the WithTypes and WithoutTypes from here.
We can define AMACRO to do the job you wanted.
#define AsVariant(param) variant param
#define ConvertFrom(type,param) converter::from<type>(param)
#define HEADDER(type,func,params) type func ##_unwrapped (WithTypes params)
#define WRAPPER(type,func,params) \
extern "C" variant func (OBSTRUCT(MacroMap1)(AsVariant,WithoutTypes params)) \
{ \
return converter::to< type >(func ## _unwrapped( \
MacroMap2(ConvertFrom,IDENT params) \
)); \
}
#define AMACRO(type,func,params) \
EVAL( \
HEADDER(type,func,params); \
WRAPPER(type,func,params) \
HEADDER(type,func,params) \
)
Which will turn this:
AMACRO(SomeObject,f,(const std::string&, s, int*, x))
{
// ... do something with the inputs ...
return SomeObject();
}
Into this (after formatting):
SomeObject f_unwrapped (const std::string& s , int* x );
extern "C" variant f (variant s , variant x )
{
return converter::to<SomeObject>(f_unwrapped(converter::from<const std::string&>(s),converter::from<int*>(x)));
}
SomeObject f_unwrapped (const std::string& s , int* x )
{
return SomeObject();
}
NOTE:
If the const needs removing from the parameter, a conditional, similar to the ISDONE, can be made and added to the ConvertFrom macro.
I have a struct in my server that I would like to generate it with macro or template in C++ as it has a lot of redundant things:
struct MyBlock {
void Merge(const MyBlock& from) {
if (apple.HasData()) {
apple.Merge(from.apple);
}
if (banana.HasData()) {
banana.Merge(from.banana());
}
...
}
void Clear() {
apple.Clear();
banana.Clear();
...
}
void Update(const SimpleBlock& simple_block) {
if (simple_block.apple.Updated()) {
apple.Add(simple_block.apple);
}
if (simple_block.banana.Updated()) {
banana.Add(simple_block.banana);
}
...
}
Fruit apple;
Fruit banana;
Animal dog;
Animal cat;
...
}
struct SimpleBlock {
SimpleFruit apple;
SimpleFruit banana;
SimpleAnimal dog;
SimpleAnimal cat;
...;
}
I would like to define more variables in the two blocks like apple and dog. I would also like to define more pairs of such blocks. But it involves a lot of trivial work. So my question is how we can use macro, template or some other C++ features including C++11 to generate these blocks in compile time?
The reason why I don't use collections to store those variable is because MyBlock struct would be passed as a parameter in another template class which would dynamically allocate and release this block in run time. It is actually a thread local block that would be aggregated periodically.
Straightforward enough with preprocessor list iteration:
#define M_NARGS(...) M_NARGS_(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define M_NARGS_(_10, _9, _8, _7, _6, _5, _4, _3, _2, _1, N, ...) N
#define M_CONC(A, B) M_CONC_(A, B)
#define M_CONC_(A, B) A##B
#define M_ID(...) __VA_ARGS__
#define M_LEFT(L, R) L
#define M_RIGHT(L, R) R
#define M_FOR_EACH(ACTN, ...) M_CONC(M_FOR_EACH_, M_NARGS(__VA_ARGS__)) (ACTN, __VA_ARGS__)
#define M_FOR_EACH_0(ACTN, E) E
#define M_FOR_EACH_1(ACTN, E) ACTN(E)
#define M_FOR_EACH_2(ACTN, E, ...) ACTN(E) M_FOR_EACH_1(ACTN, __VA_ARGS__)
#define M_FOR_EACH_3(ACTN, E, ...) ACTN(E) M_FOR_EACH_2(ACTN, __VA_ARGS__)
#define M_FOR_EACH_4(ACTN, E, ...) ACTN(E) M_FOR_EACH_3(ACTN, __VA_ARGS__)
#define M_FOR_EACH_5(ACTN, E, ...) ACTN(E) M_FOR_EACH_4(ACTN, __VA_ARGS__)
//.. extend this to higher numbers with some copy&paste
#define MYBLOCK(...) struct MyBlock { \
void Merge(const MyBlock& from) { \
M_FOR_EACH(BLOCK_MERGE, __VA_ARGS__) \
} \
void Clear() { \
M_FOR_EACH(BLOCK_CLEAR, __VA_ARGS__) \
} \
void Update(const SimpleBlock& simple_block) { \
M_FOR_EACH(BLOCK_UPDATE, __VA_ARGS__) \
} \
M_FOR_EACH(BLOCK_FIELD, __VA_ARGS__) \
}
#define BLOCK_MERGE(F) if (M_ID(M_RIGHT F).HasData()) { \
M_ID(M_RIGHT F).Merge(from.M_ID(M_RIGHT F)); \
}
#define BLOCK_CLEAR(F) M_ID(M_RIGHT F).Clear;
#define BLOCK_UPDATE(F) if (simple_block.M_ID(M_RIGHT F).Updated()) { \
M_ID(M_RIGHT F).Add(simple_block.M_ID(M_RIGHT F)); \
}
#define BLOCK_FIELD(F) M_ID(M_LEFT F) M_ID(M_RIGHT F);
#define SIMPLEBLOCK(...) struct SimpleBlock { M_FOR_EACH(SIMPLE_DECL, __VA_ARGS__) }
#define SIMPLE_DECL(F) M_CONC(Simple, M_ID(M_LEFT F)) M_ID(M_RIGHT F);
#define FIELDS (Fruit, apple),(Fruit,banana),(Animal,dog),(Animal,cat)
MYBLOCK(FIELDS);
SIMPLEBLOCK(FIELDS);
Add the necessary further member variables to FIELDS in the existing format, and they will be added to the structs emitted by MYBLOCK and SIMPLEBLOCK. (Remember to extend M_FOR_EACH with more iterations... easy to to with a few ctrl+c,ctrl+v.)
template <typename SimpleT>
class BlockTemplate
{
public:
void Merge(const BlockTemplate& from) {
if (HasData()) {
Merge(from.simpleData);
}
}
void Update(const SimpleT& simple_block) {
if (simple_block.Updated()) {
Add(simple_block.data);
}
}
protected:
SimpleT simpleData;
};
Now, you can create objects of type BlockTemplate<SimpleFruit>, BlockTemplate<SimpleAnimal> etc. You could also store pointers to all these BlockTemplate objects in a container after having BlockTemplate inherit from an abstract type. Or, better yet, use the new-fangled type-erasure methods - boost::type_erasure::any for example.
EDIT : If you don't want to use the container that way, you could also make BlockTemplate variadic and store a tuple of different(type-wise) SimpleT objects and modify the Merge and Update functions accordingly. The problem with this is that it becomes much harder to track your SimpleT objects - std::tuple doesn't allow you to give names. You would be referring to the values as get<N>(tupleData).
The description of why you don't use a collection sounds like some optimization thing. Have you measured?
Anyway, one simple solution is store pointers to the objects in a collection.
Then you can iterate over the collection.
I have a following struct:
struct Data
{
std::string firstMember;
std::string secondMember;
std::string thirdMember;
};
I want to select one of the members by string name in constexpr manner, like
Data instance;
auto& member = getMember(instance, "firstMember");
getMember is constexpr function/struct/macros/whatever in question and expression should be (I want it to be) optimized into simple auto& member = instance.firstMember;. My desire here is to be able to call getMember from another constexpr function, which in turn are computing name of particular member --> some kind of compile time reflection.
I know, there is no reflection in C++, therefore it's OK to register somehow (partially specialize? use some macros magic?) names of members of struct in question, like:
REGISTER_MEMBER(Data, "firstMember", firstMember);
All I want is to have that compile time optimization and do nothing in runtime. Is that possible in C++11 and how?
As noted in the comments, first take a look at BOOST_FUSION_ADAPT_STRUCT (and friends):
#include <boost/fusion/include/adapt_struct.hpp>
#include <string>
struct Data
{
std::string firstMember;
std::string secondMember;
std::string thirdMember;
};
BOOST_FUSION_ADAPT_STRUCT(
Data,
(std::string, firstMember)
(std::string, secondMember)
(std::string, thirdMember)
)
This turns your Data structure into a sequence usable by Fusion:
#include <boost/fusion/include/at_c.hpp>
int main()
{
Data d = { "firstData", "secondData", "thirdData" };
std::cout << boost::fusion::at_c<0>(d) << std::endl;
}
This prints "firstData". Change the index to refer to the members in order.
There, now we can refer to members at compile-time using a number. But you wanted a name. Also noted in the comments, processing strings is a runtime feature...almost. C++11 gives us constexpr.
It's a bit tricky, but ultimately it looks like this:
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/seq.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <stdexcept>
// and repeat for BOOST_FUSION_ADAPT_TPL_STRUCT, etc...
#define REFLECT_STRUCT(NAME, ATTRIBUTES) \
REFLECT_STRUCT_DETAIL(NAME, \
ATTRIBUTES, \
BOOST_PP_SEQ_POP_FRONT( \
BOOST_PP_CAT( \
/* warning: uses fusion implementation details: */ \
BOOST_FUSION_ADAPT_STRUCT_FILLER_0(0,0)ATTRIBUTES, \
_END))) \
#define REFLECT_STRUCT_DETAIL(NAME, ATTRIBUTES, WRAPPEDATTRIBUTES) \
BOOST_FUSION_ADAPT_STRUCT(NAME, ATTRIBUTES) \
\
namespace detail \
{ \
namespace BOOST_PP_CAT(reflect_, NAME) \
{ \
template <int N> \
struct member_name; \
\
BOOST_PP_SEQ_FOR_EACH_I(REFLECT_STRUCT_DETAIL_MEMBER_NAME, \
BOOST_PP_EMPTY, \
WRAPPEDATTRIBUTES) \
\
template <int N> \
constexpr bool member_match_index(const std::size_t index, \
const char* const str, \
const std::size_t len) \
{ \
return index == len || \
(member_name<N>::value()[index] == str[index] \
&& member_match_index<N>(index + 1, str, len)); \
} \
\
template <int N> \
constexpr bool member_match(const char* const str, \
const std::size_t len) \
{ \
return len == member_name<N>::value_length \
&& member_match_index<N>(0, str, len); \
} \
\
constexpr int find_member(const char* const str, \
const std::size_t len) \
{ \
return BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(WRAPPEDATTRIBUTES), \
REFLECT_STRUCT_DETAIL_MEMBER_NAME_TEST, \
BOOST_PP_EMPTY) \
throw std::runtime_error("could not find " \
BOOST_PP_STRINGIZE(NAME) \
" member"); \
} \
} \
} \
\
constexpr int BOOST_PP_CAT(indexof_, NAME)(const char* const str, \
const std::size_t len) \
{ \
return detail::BOOST_PP_CAT(reflect_, NAME)::find_member(str, len); \
} \
\
template <std::size_t N> \
constexpr int BOOST_PP_CAT(indexof_, NAME)(const char (&str)[N]) \
{ \
return detail::BOOST_PP_CAT(reflect_, NAME)::find_member(&str[0], N); \
}
#define REFLECT_STRUCT_DETAIL_EXTRACT_NAME(pair) \
BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(1, pair))
#define REFLECT_STRUCT_DETAIL_MEMBER_NAME(r, data, n, elem) \
REFLECT_STRUCT_DETAIL_MEMBER_NAME_DETAIL(n, REFLECT_STRUCT_DETAIL_EXTRACT_NAME(elem))
#define REFLECT_STRUCT_DETAIL_MEMBER_NAME_DETAIL(n, name) \
template <> \
struct member_name<n> \
{ \
static constexpr std::size_t value_length = sizeof(name); \
typedef const char value_type[value_length]; \
\
static constexpr const value_type& value() \
{ \
return name; \
} \
};
#define REFLECT_STRUCT_DETAIL_MEMBER_NAME_TEST(z, n, text) \
member_match<n>(str, len) ? n :
It looks scary but its readable if you take the time to pick it apart.
We have to introduce our own macros to give constant-expression access to the member names; most of the ugly comes from processing Boost.Preprocessor lists. Although Fusion does record the names during adaptation as well (see boost::fusion::extension::struct_member_name), they are not marked as constexpr so aren't usable to us, unfortunately.
This gives:
#include <boost/fusion/include/at_c.hpp>
#include <iostream>
#include <string>
struct Data
{
std::string firstMember;
std::string secondMember;
std::string thirdMember;
};
REFLECT_STRUCT(
Data,
(std::string, firstMember)
(std::string, secondMember)
(std::string, thirdMember)
)
// your desired code:
// (note the use of at_c ensures this is evaluated at comple-time)
#define GETMEMBER(data, member) boost::fusion::at_c<indexof_Data(member)>(data)
int main()
{
Data d = { "firstData", "secondData", "thirdData" };
std::cout << boost::fusion::at_c<indexof_Data("firstMember")>(d) << std::endl;
std::cout << GETMEMBER(d, "secondMember") << std::endl;
std::cout << GETMEMBER(d, "thirdMember") << std::endl;
/* causes error: std::cout << GETMEMBER(d, "nonexistent_member") << std::endl; */
}
Which I think is close to what you were after.
But keep in mind this may not all be necessary: Boost.Fusion may already have what you need. It lives in the area between pure compile-time stuff (Boost.MPL) and regular run-time stuff; adapt your struct and you can already do things like iterate over it (boost::fusion::for_each).