How to use boost preprocessor to generate accessors?
Disclaimer:You should probably wait in case a better answer appears even if you are satisfied with this answer, because I'm far from an expert and these may not be the best approaches.
1st approach:
//two different sequences
struct A
{
MY_MACRO1((int)(float)(double),(x)(y)(z))
};
I think this approach gives the less scary-looking macro:
#define DECLARE_DATA_MEMBER1(R,TYPES,INDEX,NAME) \
BOOST_PP_SEQ_ELEM(INDEX,TYPES) BOOST_PP_CAT(m_,NAME);
#define DEFINE_ACCESSOR1(R,TYPES,INDEX,NAME) \
BOOST_PP_SEQ_ELEM(INDEX,TYPES) NAME(){ return BOOST_PP_CAT(m_,NAME); }
#define MY_MACRO1(TYPES,NAMES) \
BOOST_PP_SEQ_FOR_EACH_I(DECLARE_DATA_MEMBER1,TYPES,NAMES) \
public: \
BOOST_PP_SEQ_FOR_EACH_I(DEFINE_ACCESSOR1,TYPES,NAMES)
MY_MACRO
gets two sequences: TYPES
and NAMES
. In order to declare the data members I use a BOOST_PP_SEQ_FOR_EACH_I
on the sequence NAMES
using the macro DECLARE_DATA_MEMBER1
and having the sequence TYPES
as data. This "invokes" DECLARE_DATA_MEMBER1
with 4 parameters: R
which is unused (and I have no idea what it does), TYPES
(the sequence of types), INDEX
(tells in which iteration we are right now, starting at 0), and NAME
(the element of the original NAMES
sequence that corresponds with this iteration).
The "bodies" of DECLARE_DATA_MEMBER1
and DEFINE_ACCESSOR1
are simple, we simply get the INDEX
th element in the types sequence, and concatenate m_
with NAME
.
2nd approach:
//just one sequence but you need to put two sets of parentheses around each pair
struct B
{
MY_MACRO2(((int, x))((float,y))((double,z)))
};
This one is still fairly simple, but has the inconvenient of having to use double parentheses.
#define DECLARE_DATA_MEMBER2(R,_,TYPE_AND_NAME) \
BOOST_PP_TUPLE_ELEM(2,0,TYPE_AND_NAME) BOOST_PP_CAT(m_,BOOST_PP_TUPLE_ELEM(2,1,TYPE_AND_NAME));
#define DEFINE_ACCESSOR2(R,_,TYPE_AND_NAME) \
BOOST_PP_TUPLE_ELEM(2,0,TYPE_AND_NAME) BOOST_PP_TUPLE_ELEM(2,1,TYPE_AND_NAME)(){ return BOOST_PP_CAT(m_,BOOST_PP_TUPLE_ELEM(2,1,TYPE_AND_NAME)); }
#define MY_MACRO2(TYPES_AND_NAMES) \
BOOST_PP_SEQ_FOR_EACH(DECLARE_DATA_MEMBER2,_,TYPES_AND_NAMES) \
public: \
BOOST_PP_SEQ_FOR_EACH(DEFINE_ACCESSOR2,_,TYPES_AND_NAMES)
This time there is only one sequence so we won't need the index in the helper macros. For this reason BOOST_PP_SEQ_FOR_EACH
is used on TYPES_AND_NAMES using the macro DECLARE_DATA_MEMBER2
and without passing any extra data. This macro receives three "arguments": R
again unused, _
(or DATA
, also unused here), and TYPE_AND_NAME
(a tuple in the form (TYPE,NAME)
).
In the "bodies" of the two helper macros BOOST_PP_TUPLE_ELEM
is used to get either the type(with index=0) or the name(with index=1). This macro needs to be passed the size of the tuple, the index of the element you want and the tuple.
3rd approach:
//one sequence but the macro is more complex
struct C
{
MY_MACRO3((int,x)(float,y)(double,z))
};
This macro borrows heavily from BOOST_FUSION_ADAPT_STRUCT
and similar macros.
//Heavily "inspired" from BOOST_FUSION_ADAPT_STRUCT
#define CREATE_MY_MACRO_PLACEHOLDER_FILLER_0(X, Y) \
((X, Y)) CREATE_MY_MACRO_PLACEHOLDER_FILLER_1
#define CREATE_MY_MACRO_PLACEHOLDER_FILLER_1(X, Y) \
((X, Y)) CREATE_MY_MACRO_PLACEHOLDER_FILLER_0
#define CREATE_MY_MACRO_PLACEHOLDER_FILLER_0_END
#define CREATE_MY_MACRO_PLACEHOLDER_FILLER_1_END
#define DECLARE_DATA_MEMBER3(R,_,TYPE_AND_NAME) \
BOOST_PP_TUPLE_ELEM(2,0,TYPE_AND_NAME) BOOST_PP_CAT(m_,BOOST_PP_TUPLE_ELEM(2,1,TYPE_AND_NAME));
#define DEFINE_ACCESSOR3(R,_,TYPE_AND_NAME) \
BOOST_PP_TUPLE_ELEM(2,0,TYPE_AND_NAME) BOOST_PP_TUPLE_ELEM(2,1,TYPE_AND_NAME)(){ return BOOST_PP_CAT(m_,BOOST_PP_TUPLE_ELEM(2,1,TYPE_AND_NAME)); }
#define MY_MACRO3(TYPES_AND_NAMES) \
BOOST_PP_SEQ_FOR_EACH(DECLARE_DATA_MEMBER3,_,BOOST_PP_CAT(CREATE_MY_MACRO_PLACEHOLDER_FILLER_0 TYPES_AND_NAMES,_END)) \
public: \
BOOST_PP_SEQ_FOR_EACH(DEFINE_ACCESSOR3,_,BOOST_PP_CAT(CREATE_MY_MACRO_PLACEHOLDER_FILLER_0 TYPES_AND_NAMES,_END))
In this approach the helper macros are basically unchanged. The only (big) difference is that the sequence used in the for_each is not simply TYPES_AND_NAMES
but BOOST_PP_CAT(CREATE_MY_MACRO_PLACEHOLDER_FILLER_0 TYPES_AND_NAMES,_END)
. This is a clever trick to force the double parentheses. It works like this:
CREATE_MY_MACRO_PLACEHOLDER_FILLER_0(int,x)(float,y)_END
//CREATE_MY_MACRO_PLACEHOLDER_FILLER_0(A,B)->((A,B))CREATE_MY_MACRO_PLACEHOLDER_FILLER_1
((int,x))CREATE_MY_MACRO_PLACEHOLDER_FILLER_1(float,y)_END
//CREATE_MY_MACRO_PLACEHOLDER_FILLER_1(A,B)->((A,B))CREATE_MY_MACRO_PLACEHOLDER_FILLER_0
((int,x))((float,y))CREATE_MY_MACRO_PLACEHOLDER_FILLER_0_END
//CREATE_MY_MACRO_PLACEHOLDER_FILLER_0_END->
((int,x))((float,y))
Running on Coliru.
Variadic macros: Reuse variadic arguments (Boost.Fusion)
It is relatively easy to construct a macro that generates all your boilerplate from an invocation of your preferred macro:
DEFINE_DATAVAR_STRUCT(
bar,
(double, bar1)
(float, bar2)
)
In this macro you have two arguments: a name (bar
) and a "sequence" of tuples ((double,bar1)(float,bar2)
). I put "sequence" in quotes because in order to have a sequence of tuples that can work with Boost.Preprocessor macros you need to have each tuple delimited by 2 sets of parentheses. In the example below most of the complexity is caused by the solution to this problem (there is more information about this in approach 3 in this answer.
Running on Wandbox
#include <iostream>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/fusion/include/define_assoc_struct.hpp>
#include <boost/fusion/include/at_key.hpp>
#define VAR(VARNAME) vardefs::VARNAME
#define VAR_PARENTHESES(VARNAME) (vardefs)(VARNAME)
//THIS IS ONLY NEEDED IN ORDER TO GET 2 SETS OF PARENTHESES
//Heavily "inspired" from BOOST_FUSION_ADAPT_STRUCT
#define GENERATE_DATAVAR_SEQUENCE_FILLER_0(X, Y) \
((X, Y)) GENERATE_DATAVAR_SEQUENCE_FILLER_1
#define GENERATE_DATAVAR_SEQUENCE_FILLER_1(X, Y) \
((X, Y)) GENERATE_DATAVAR_SEQUENCE_FILLER_0
#define GENERATE_DATAVAR_SEQUENCE_FILLER_0_END
#define GENERATE_DATAVAR_SEQUENCE_FILLER_1_END
#define GENERATE_DATAVAR_SEQUENCE(MEMBERS) BOOST_PP_CAT(GENERATE_DATAVAR_SEQUENCE_FILLER_0 MEMBERS,_END)
//THESE AREN'T ACTUALLY REQUIRED BUT HELP WITH READABILITY
#define DATAVAR_GET_TYPE(TUPLE) BOOST_PP_TUPLE_ELEM(2,0,TUPLE)
#define DATAVAR_GET_NAME(TUPLE) BOOST_PP_TUPLE_ELEM(2,1,TUPLE)
//THESE ARE THE HELPERS THAT ACTUALLY GENERATE THE VARIABLE PARTS OF THE MACRO
#define GENERATE_STRUCT_KEYS(_,__,TUPLE) struct DATAVAR_GET_NAME(TUPLE);
#define GENERATE_STRUCT_DEFINITION_SEQ(_,NAME,TUPLE) (DATAVAR_GET_TYPE(TUPLE),DATAVAR_GET_NAME(TUPLE),VAR(NAME)::keys::DATAVAR_GET_NAME(TUPLE))
// ===============================================================================================
#define DEFINE_DATAVAR_STRUCT(NAME,MEMBERS) \
namespace VAR(NAME)::keys{ \
BOOST_PP_SEQ_FOR_EACH(GENERATE_STRUCT_KEYS,_,GENERATE_DATAVAR_SEQUENCE(MEMBERS)) \
} \
namespace VAR(NAME) { struct index; } \
BOOST_FUSION_DEFINE_ASSOC_STRUCT( \
VAR_PARENTHESES(NAME), type, \
BOOST_PP_SEQ_FOR_EACH(GENERATE_STRUCT_DEFINITION_SEQ,NAME,GENERATE_DATAVAR_SEQUENCE(MEMBERS))\
)
// -----------------------------------------------------------------------------------------------
// ========================================================
DEFINE_DATAVAR_STRUCT(
foo,
(char, foo1)
(int, foo2)
(float, foo3)
)
// --------------------------------------------------------
// ========================================================
DEFINE_DATAVAR_STRUCT(
bar,
(double, bar1)
(float, bar2)
)
// --------------------------------------------------------
int main()
{
VAR(foo)::type fooI{'a',1,2.0f};
VAR(bar)::type barI{1.0,2.0f};
std::cout << boost::fusion::at_key<VAR(foo)::keys::foo1>(fooI) << std::endl;
std::cout << boost::fusion::at_key<VAR(foo)::keys::foo2>(fooI) << std::endl;
std::cout << boost::fusion::at_key<VAR(foo)::keys::foo3>(fooI) << std::endl;
std::cout << boost::fusion::at_key<VAR(bar)::keys::bar1>(barI) << std::endl;
std::cout << boost::fusion::at_key<VAR(bar)::keys::bar2>(barI) << std::endl;
}
Generate boilerplate code by transforming arguments to string literals
If you are interested in using the Boost.Preprocessor library you need to familiarize yourself with two fundamental "data types": sequence and tuple. You can find the whole list of macros that the library uses in the reference section of the documentation. I'll explain the ones I use below.
There are two macros in the interface: XML_TAG
and DEFINE_XML_TAGS
.XML_TAG
is really simple, it just puts its arguments inside two sets of parentheses. This causes that however many XML_TAG
s you use will be converted to a sequence which elements are tuples (struct_name,sequence_of_type_and_name_pairs)
.DEFINE_XML_TAGS
is the macro that does all the work. It uses three helper macros GENERATE_STRUCT_DEFS
, GENERATE_VARIANT_OF_TYPES
and GENERATE_XMLTAGS
.
GENERATE_VARIANT_OF_TYPES
Invokes ENUMERATE_TYPES(TAG_SEQ)
in order to get a comma separated list of types.
Right now TAG_SEQ
is ((Person,(int,age)))((Company,(int,noEmployees)(std::string,location)))
and we want to have Person,Company
.BOOST_PP_ENUM(SEQ)
takes a sequence and returns its elements separated by commas. So we need to have BOOST_PP_ENUM((Person)(Company))
.BOOST_PP_SEQ_FOR_EACH(MACRO,DATA,SEQ)
calls MACRO with each of the elements in SEQ and whichever DATA you pass. So BOOST_PP_SEQ_FOR_EACH(GET_TYPE_SEQUENCE,_,TAG_SEQ)
calls GET_TYPE_SEQUENCE
with (Person,(int,age))
and (Company,(int,noEmployees)(sd:string,location))
.GET_TYPE_SEQUENCE
then simply takes the first element of each tuple and puts it inside a set of parentheses using BOOST_PP_TUPLE_ELEM
.
GENERATE_XML_TAGS
It calls GENERATE_PAIRS
which in turn calls a SEQ_FOR_EACH using GENERATE_ONE_PAIR
.
As explained in the previous section GENERATE_ONE_PAIR
gets each of the tuples (struct_name, sequence_of_type_name_pairs). It takes the name and adds a pair of parentheses after it and then calls GENERATE_VECTOR_OF_MEMBER_NAMES
with the sequence_of_type_name_pairs. GENERATE_VECTOR_OF_MEMBER_NAMES
first adds the mandatory "name" member and then does something with BOOST_PP_ENUM
very similar to the macro explained above with the difference that it needs to do a little trick because the current sequence of tuples does not have two sets of parentheses (this is explained here in the 3rd approach). GENERATE_MEMBER_NAME_SEQUENCE
then simply takes the name of the member, converts it to string and then puts a set of parentheses around it.
GENERATE_STRUCT_DEFSBOOST_PP_REPEAT(N,MACRO,DATA)
calls MACRO N times, passing DATA and the current repetition index. GENERATE_ONE_STRUCT_DEF
takes the index-th element of the sequence and then takes firstly the name of the struct and lastly the sequence of type-name pairs and calls DO_GENERATE_ONE_STRUCT_DEF
with those values. Finally DO_GENERATE_ONE_STRUCT_DEF
builds the BOOST_FUSION_DEFINE_STRUCT
macro invocation.
I think, but I'm not knowledgeable enough to be sure, that there is a
bug inBOOST_FUSION_ADAPT_STRUCT_NAMESPACE_DEFINITION_END
. It uses
BOOST_PP_REPEAT_1
directly when I think it should just use
BOOST_PP_REPEAT
. I have undefined and redefined that macro using
BOOST_PP_REPEAT
and everything seems to work, but you probably
shouldn't trust it blindly.
Test Running on WandBox
define_xml_tags.hpp
#include <boost/fusion/include/define_struct.hpp>
#include <boost/variant.hpp>
#include <vector>
#include <utility>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/seq/size.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
//I think there is a bug in the original macro, it uses BOOST_PP_REPEAT_1 where I think it should use BOOST_PP_REPEAT, but I don't know enough to know for sure
#undef BOOST_FUSION_ADAPT_STRUCT_NAMESPACE_DEFINITION_END
#define BOOST_FUSION_ADAPT_STRUCT_NAMESPACE_DEFINITION_END(NAMESPACE_SEQ) \
BOOST_PP_REPEAT( \
BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(NAMESPACE_SEQ)), \
BOOST_FUSION_ADAPT_STRUCT_NAMESPACE_END_I, \
_)
//helps form a SEQUENCE of TUPLES
#define XML_TAG(NAME,MEMBER_SEQ) ((NAME,MEMBER_SEQ))
//helpers for GENERATE_STRUCT_DEFS, read from the bottom to the top
#define DO_GENERATE_ONE_STRUCT_DEF(NAME,MEMBER_SEQ) \
BOOST_FUSION_DEFINE_STRUCT( (), NAME, (std::string, name) MEMBER_SEQ)
#define GENERATE_ONE_STRUCT_DEF(Z,INDEX,TAG_SEQ) \
DO_GENERATE_ONE_STRUCT_DEF(BOOST_PP_TUPLE_ELEM(2,0,BOOST_PP_SEQ_ELEM(INDEX,TAG_SEQ)), BOOST_PP_TUPLE_ELEM(2,1,BOOST_PP_SEQ_ELEM(INDEX,TAG_SEQ)))
#define GENERATE_STRUCT_DEFS(TAG_SEQ) \
BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(TAG_SEQ),GENERATE_ONE_STRUCT_DEF,TAG_SEQ)
//helpers for GENERATE_VARIANT_OF_TYPES, bottom to top
#define GET_TYPE_SEQUENCE(R,DATA,NAME_MEMBERSEQ_TUPLE) \
(BOOST_PP_TUPLE_ELEM(2,0,NAME_MEMBERSEQ_TUPLE))
#define ENUMERATE_TYPES(TAG_SEQ) \
BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH(GET_TYPE_SEQUENCE,_,TAG_SEQ))
#define GENERATE_VARIANT_OF_TYPES(TAG_SEQ) \
typedef boost::variant<ENUMERATE_TYPES(TAG_SEQ)> Types;
//helpers for GENERATE_XMLTAGS, go from bottom to top in order to understand
//Heavily "inspired" from BOOST_FUSION_ADAPT_STRUCT
#define GENERATE_NAME_SEQUENCE_FILLER_0(X, Y) \
((X, Y)) GENERATE_NAME_SEQUENCE_FILLER_1
#define GENERATE_NAME_SEQUENCE_FILLER_1(X, Y) \
((X, Y)) GENERATE_NAME_SEQUENCE_FILLER_0
#define GENERATE_NAME_SEQUENCE_FILLER_0_END
#define GENERATE_NAME_SEQUENCE_FILLER_1_END
#define GENERATE_MEMBER_NAME_SEQUENCE(R,DATA,INDEX,TYPE_NAME_TUPLE) (BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(2,1,TYPE_NAME_TUPLE)))
#define GENERATE_VECTOR_OF_MEMBER_NAMES(MEMBER_SEQ) \
{ "name", BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH_I(GENERATE_MEMBER_NAME_SEQUENCE,_,BOOST_PP_CAT(GENERATE_NAME_SEQUENCE_FILLER_0 MEMBER_SEQ,_END))) }
#define GENERATE_ONE_PAIR(R,DATA,NAME_MEMBERSEQ_TUPLE) \
{ BOOST_PP_TUPLE_ELEM(2,0,NAME_MEMBERSEQ_TUPLE)(), GENERATE_VECTOR_OF_MEMBER_NAMES(BOOST_PP_TUPLE_ELEM(2,1,NAME_MEMBERSEQ_TUPLE)) },
#define GENERATE_PAIRS(TAG_SEQ) \
BOOST_PP_SEQ_FOR_EACH(GENERATE_ONE_PAIR,_,TAG_SEQ)
#define GENERATE_XMLTAGS(TAG_SEQ) \
const std::vector<std::pair<Types,std::vector<std::string>>> xmlTags = { GENERATE_PAIRS(TAG_SEQ) };
//This is the actual macro, it simply invokes three different macros that do a different task each
#define DEFINE_XML_TAGS(TAG_SEQ) \
GENERATE_STRUCT_DEFS(TAG_SEQ) \
GENERATE_VARIANT_OF_TYPES(TAG_SEQ) \
GENERATE_XMLTAGS(TAG_SEQ)
main.cpp
#include <iostream>
#include <boost/fusion/include/io.hpp>
#include <boost/fusion/include/as_vector.hpp>
#include <boost/variant/static_visitor.hpp>
#include "define_xml_tags.hpp"
DEFINE_XML_TAGS(
XML_TAG(
Person,
(int, age)
)
XML_TAG(
Company,
(int, noEmployees)
(std::string, location)
)
)
struct printer : boost::static_visitor<void> {
void operator()(const Person& p) const
{
std::cout << "This is a person:" << boost::fusion::as_vector(p) << '\n';
}
void operator()(const Company& c) const
{
std::cout << "This is a company:" << boost::fusion::as_vector(c) << '\n';
}
};
void identify(Types v)
{
boost::apply_visitor(printer(),v);
}
int main()
{
Person p;
p.name="John";
p.age = 18;
identify(p);
Company c;
c.name="Mpany Co";
c.noEmployees=123;
c.location="Fake St";
identify(c);
std::cout << "\nChecking xmlTags:\n";
for(const auto& pair : xmlTags)
{
identify(pair.first);
std::cout << "It has the following members:\n";
for(const auto& str : pair.second)
std::cout << str << '\n';
}
std::cout << std::endl;
}
How to write read-only accessor functions in an aggregate root class?
Turning my comment into an answer.
If you decide to go with alternative 1 (N*K delegates), you can use Boost.Preprocessor to do the boilerplate work for you:
#include <boost/preprocessor.hpp>
// Define identifier names
#define FUNCTIONS (fun)(gun)(hun)
#define MEMBER_NAMES (m1_)(m2_)(m3_)
#define SUFFIXES (_1)(_2)(_3)
// Utility "data structure"
// Used to hand down state from iteration over functions to iteration over suffixes
#define WRAP_DATA(function, member) \
(2, (function, member))
#define UNWRAP_DATA_FUNTION(data) \
BOOST_PP_ARRAY_ELEM(0, data)
#define UNWRAP_DATA_MEMBER(data) \
BOOST_PP_ARRAY_ELEM(1, data)
// Accessor-generating functionality
// Convenience macro for generating the correct accessor name
#define CREATE_FUNCTION_NAME(data, suffix) \
BOOST_PP_CAT(UNWRAP_DATA_FUNCTION(data), suffix)
// Macro generating one accessor delegation
#define GENERATE_ACCESSOR(r, data, suffix) \
int CREATE_FUNCTION_NAME(data, suffix) () const { return UNWRAP_DATA_MEMBER(data).CREATE_FUNCTION_NAME(data, suffix) (); }
// Generate accessors
class C
{
// Execute GENERATE_ACCESSOR once for each element of SUFFIXES
#define BOOST_PP_LOCAL_MACRO(iter) \
BOOST_PP_SEQ_FOR_EACH(GENERATE_ACCESSOR, WRAP_DATA(BOOST_PP_SEQ_ELEM(iter, FUNCTIONS), BOOST_PP_SEQ_ELEM(iter, MEMBER_NAMES)), SUFFIXES)
#define BOOST_PP_LOCAL_LIMITS (0, BOOST_PP_SEQ_SIZE(FUNCTIONS) - 1)
// Execute BOOST_PP_LOCAL_MACRO once for each value within BOOST_PP_LOCAL_LIMITS
#include BOOST_PP_LOCAL_ITERATE()
// rest of class C here
// ...
};
Translated into pseudo-code to better highlight the working logic:
FUNCTIONS = {fun, gun, hun};
MEMBER_NAMES = {m1_, m2_, m3_};
SUFFIXES = {_1, _2, _3};
struct Data {
auto function, member;
};
auto createFunctionName(data, suffix) {
return data.function + suffix;
}
auto generateAccessor(data, suffix) {
return "int " + createFunctionName(data, suffix) + "() const { return " + data.member + "." + createFunctionName(data, suffix) + "(); }";
}
class C
{
for (i = 0; i < sizeof(FUNCTIONS); ++i) {
foreach (suffix in SUFFIXES) {
generateAccessor(Data(FUNCTIONS[i], MEMBER_NAMES[i]), suffix);
}
}
};
Use C++ macro string concatenation to access object properties
Two fixes (Verified by generating preprocessor output)
- Remove
##
fromcbs.##name
cbs.##name(value)
Callback doesn't takevalue
as parameter.
#define ACCESSOR_WITH_CALLBACKS(name, location) \
auto& name() { return location; } \
auto name() const { return location; } \
void name(int value, const Callbacks& cbs) { \
name() = value; \
if (cbs.name != nullptr) { \
cbs.name(); \
} \
}
struct State2 {
private:
std::array<int, 2> data_;
public:
ACCESSOR_WITH_CALLBACKS(x, data_[0]);
ACCESSOR_WITH_CALLBACKS(y, data_[1]);
};
Related Topics
Why, Really, Deleting an Incomplete Type Is Undefined Behaviour
How to Enable Experimental C++11 Concurrency Features in Mingw
How Is Heap and Stack Memories Managed, Implemented, Allocated
Does "Undefined Behaviour" Extend to Compile-Time
How to Extract the Mantissa of a Double
Cmake Cannot Determine Linker Language for Target
Execute C++ from String Variable
C++ Warning: Address of Local Variable
Why Do You Use Typedef When Declaring an Enum in C++
Difference Between 'Strcpy' and 'Strcpy_S'
Why Doesn't Delete Destroy Anything
Write and Read String to Binary File C++
Gcc - How to Create a Mapfile of the Object File
What Is the Use of 0-Length Array (Or Std::Array)
Pointers to Members Representations