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).
Related
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.
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.
Is there a way to generate this kind of macro with some other macro?
E.g. a struct with 3 members or something...
#define CONST_STRUCT2(name, name1, type1, name2, type2) \
struct name \
{ \
name(const type1& p##name1, const type2& p##name2) \
: name1(p##name1), name2(p##name2) {} \
const type1 name1; const type2 name2; \
};
Yes, you can do it with Boost.Preprocessor.
#define CONST_STRUCT(name, members) \
struct name \
{ \
name(GENERATE_CTOR_PARAMS(members)) \
: GENERATE_CTOR_INITIALISERS(members) {} \
GENERATE_MEMBERS(members) \
}
#define PARAM_TYPE(param) \
BOOST_PP_TUPLE_ELEM(2, 0, param)
#define PARAM_NAME(param) \
BOOST_PP_TUPLE_ELEM(2, 1, param)
#define GENERATE_CTOR_PARAMS(members) \
BOOST_PP_ENUM(BOOST_PP_SEQ_SIZE(members), GENERATE_CTOR_PARAM, members)
#define GENERATE_CTOR_PARAM(z, idx, members) \
const PARAM_TYPE(BOOST_PP_SEQ_ELEM(idx, members)) & PARAM_NAME(BOOST_PP_SEQ_ELEM(idx, members))
#define GENERATE_CTOR_INITIALISERS(members) \
BOOST_PP_ENUM(BOOST_PP_SEQ_SIZE(members), GENERATE_CTOR_INITIALISER, members)
#define GENERATE_CTOR_INITIALISER(z, idx, members) \
PARAM_NAME(BOOST_PP_SEQ_ELEM(idx, members)) (PARAM_NAME(BOOST_PP_SEQ_ELEM(idx, members)))
#define GENERATE_MEMBERS(members) \
BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(members), GENERATE_MEMBER, members)
#define GENERATE_MEMBER(z, idx, members) \
const PARAM_TYPE(BOOST_PP_SEQ_ELEM(idx, members)) PARAM_NAME(BOOST_PP_SEQ_ELEM(idx, members));
Example of use:
CONST_STRUCT(Three, ((type1, name1))((type2, name2))((type3, name3)));
Necessary header files omitted for clarity. Also, in a real application, choose better scoped names (such as CONST_STRUCT_GENERATE_...).
I wonder if it is possible using boost::mpl/preprocessor or some noce C++11 features to create function proxy from class type and function name.
Say we had:
inline void set_email(const ::std::string& value);
inline void set_email(const char* value);
inside class Email. We know there is set_email function n it, we want to create a prox class with API like
PROXY(Email, set_email, MyEmail)
Email * email = new Email();
MyEmail * myEmail = new MyEmail(email);
and have abilety to call any of set_email overloads.Is it possible and how to create such class that would proxy any number of overload functions not knowing there types (only names)?
How about this:
proxy_macro.hpp
#include <type_traits>
#include <utility>
#define PROXY(proxified, member_function, proxy_name) \
class proxy_name \
{ \
private: \
proxified & ref_; \
\
public: \
proxy_name(proxified &ref) \
: ref_(ref) \
{ \
} \
\
/* general version */ \
template<typename ... Args> \
auto member_function(Args&& ... args) \
-> typename std::enable_if<!std::is_void<decltype(ref_.member_function(std::forward<Args>(args)...))>::value, \
decltype(ref_.member_function(std::forward<Args>(args)...))>::type \
{ \
return (ref_.member_function(std::forward<Args>(args)...)); \
} \
\
/* void return type version */ \
template<typename ... Args> \
auto member_function(Args&& ... args) \
-> typename std::enable_if<std::is_void<decltype(ref_.member_function(std::forward<Args>(args)...))>::value, \
void>::type \
{ \
ref_.member_function(std::forward<Args>(args)...); \
} \
\
};
This compiles and work fine for me on g++ 4.7:
#include "proxy_macro.hpp"
#include <iostream>
#include <string>
class Email
{
public:
void set_email(const ::std::string& value)
{
std::cout << value << std::endl;
}
void set_email(const char* value)
{
std::cout << value << std::endl;
}
int set_email()
{
return (42);
}
};
PROXY(Email, set_email, MyEmail)
int main(void)
{
Email mail;
MyEmail my_mail(mail);
std::string str = "test string";
const char * ptr = "test char pointer";
my_mail.set_email(str);
my_mail.set_email(ptr);
std::cout << "test return: " << my_mail.set_email() << std::endl;
return (0);
}
Edit (smaller version thanks to comments)
proxy_macro.hpp
#include <type_traits>
#include <utility>
#define PROXY(proxified, member_function, proxy_name) \
class proxy_name \
{ \
private: \
proxified & ref_; \
\
public: \
proxy_name(proxified &ref) \
: ref_(ref) \
{ \
} \
\
template<typename ... Args> \
auto member_function(Args&& ... args) \
-> decltype(ref_.member_function(std::forward<Args>(args)...)) \
{ \
return (ref_.member_function(std::forward<Args>(args)...)); \
} \
};
Is it possible to mix up the BOOST_AUTO_TEST_CASE and BOOST_AUTO_TEST_CASE_TEMPLATE macros with the BOOST_PARAM_TEST_CASE in any way? I'm even interested in really messy ways of making this happen.
Having to build all of your test cases by hand seems really tedious. But the BOOST_PARAM_TEST_CASE mechanism is pretty darn useful, but only works if you have a test init function, which in turn requires you to have be using manual test case construction.
Is there any documentation on how to hook into the automated system yourself so you can provide your own tests that auto-register themselves?
I'm using boost 1.46 right now.
I wrote my own support for this since there really didn't seem to be any good support. This requires the C++11 decltype feature and the ::std::remove_const and ::std::remove_reference library methods to work.
The macro definitions are a modified versions of the BOOST_FIXTURE_TEST_CASE and BOOST_AUTO_TEST_CASE macros.
You use this by declaring your function thus:
BOOST_AUTO_PARAM_TEST_CASE(name, begin, end)
{
BOOST_CHECK_LT(param, 5); // The function will have an argument named 'param'.
}
Here is the header that defines the BOOST_AUTO_PARAM_TEST_CASE macro:
#include <boost/test/unit_test_suite.hpp>
#include <boost/test/parameterized_test.hpp>
#include <type_traits>
#define BOOST_FIXTURE_PARAM_TEST_CASE( test_name, F, mbegin, mend ) \
struct test_name : public F { \
typedef ::std::remove_const< ::std::remove_reference< decltype(*(mbegin)) >::type>::type param_t; \
void test_method(const param_t &); \
}; \
\
void BOOST_AUTO_TC_INVOKER( test_name )(const test_name::param_t ¶m) \
{ \
test_name t; \
t.test_method(param); \
} \
\
BOOST_AUTO_TU_REGISTRAR( test_name )( \
boost::unit_test::make_test_case( \
&BOOST_AUTO_TC_INVOKER( test_name ), #test_name, \
(mbegin), (mend))); \
\
void test_name::test_method(const param_t ¶m) \
// *******
#define BOOST_AUTO_PARAM_TEST_CASE( test_name, mbegin, mend ) \
BOOST_FIXTURE_PARAM_TEST_CASE( test_name, \
BOOST_AUTO_TEST_CASE_FIXTURE, \
mbegin, mend)
The solution provided by #Omnifarious works works, but requires a C++11 compiler.
Adapting that solution for a C++03 compiler:
#include <boost/test/unit_test_suite.hpp>
#include <boost/test/parameterized_test.hpp>
#define BOOST_FIXTURE_PARAM_TEST_CASE( test_name, F, P, mbegin, mend ) \
struct test_name : public F \
{ \
typedef P param_t; \
void test_method(const param_t &); \
}; \
\
void BOOST_AUTO_TC_INVOKER( test_name )(const test_name::param_t ¶m) \
{ \
test_name t; \
t.test_method(param); \
} \
\
BOOST_AUTO_TU_REGISTRAR( test_name )( \
boost::unit_test::make_test_case( \
&BOOST_AUTO_TC_INVOKER( test_name ), #test_name, \
(mbegin), (mend))); \
\
void test_name::test_method(const param_t ¶m) \
// *******
#define BOOST_AUTO_PARAM_TEST_CASE( test_name, param_type, mbegin, mend ) \
BOOST_FIXTURE_PARAM_TEST_CASE( test_name, \
BOOST_AUTO_TEST_CASE_FIXTURE, \
param_type, \
mbegin, mend)
This solution is slightly different is usage. Since there is no declspec in C++03, the type of the parameter object cannot be automatically deduced. We must pass it in as a parameter to BOOST_AUTO_PARAM_TEST_CASE:
class FooTestParam
{
public:
std::string mS;
FooTestParam (int n)
{
std::stringstream ss;
ss << n;
mS = ss.str();
}
};
FooTestParam fooParams [] =
{
FooTestParam (42),
FooTestParam (314)
};
BOOST_AUTO_PARAM_TEST_CASE (TestFoo, FooTestParam, fooParams, fooParams + 2)
{
const std::string testVal = param.mS;
}
BOOST_AUTO_TEST_CASE (TestAddressField)
{
const uint32_t raw = 0x0100007f; // 127.0.0.1
const uint8_t expected[4] = {127, 0, 0, 1};
const Mdi::AddressField& field = *reinterpret_cast <const Mdi::AddressField*> (&raw);
for (size_t i = 0; i < 4; ++i)
BOOST_CHECK_EQUAL (field[i], expected[i]);
}
Starting with Boost version 1.59, this is being handled by data-driven test cases:
#define BOOST_TEST_MODULE MainTest
#include <boost/test/included/unit_test.hpp>
#include <boost/test/data/test_case.hpp>
#include <boost/array.hpp>
static const boost::array< int, 4 > DATA{ 1, 3, 4, 5 };
BOOST_DATA_TEST_CASE( Foo, DATA )
{
BOOST_TEST( sample % 2 );
}
This functionality requires C++11 support from compiler and library, and does not work inside a BOOST_AUTO_TEST_SUITE.
If you have to support both old and new versions of Boost in your source, and / or pre-C++11 compilers, check out And-y's answer.
You can easily mix manual and automated test unit registration. Implement your own init function (like in example 20 on this page) and inside init function you can perform registration for parameterized test cases. Boost.Test will merge them both into single test tree.
Since Boost 1.59 internal details of realization was changed and Omnifarious's solution doesn't compile.
Reason ot that is changing signature of boost::unit_test::make_test_case function: now it take 2 additional args: __FILE__, __LINE__
Fixed solution:
#if BOOST_VERSION > 105800
#define MY_BOOST_TEST_ADD_ARGS __FILE__, __LINE__,
#define MY_BOOST_TEST_DEFAULT_DEC_COLLECTOR ,boost::unit_test::decorator::collector::instance()
#else
#define MY_BOOST_TEST_ADD_ARGS
#define MY_BOOST_TEST_DEFAULT_DEC_COLLECTOR
#endif
#define BOOST_FIXTURE_PARAM_TEST_CASE( test_name, F, mbegin, mend ) \
struct test_name : public F { \
typedef ::std::remove_const< ::std::remove_reference< decltype(*(mbegin)) >::type>::type param_t; \
void test_method(const param_t &); \
}; \
\
void BOOST_AUTO_TC_INVOKER( test_name )(const test_name::param_t ¶m) \
{ \
test_name t; \
t.test_method(param); \
} \
\
BOOST_AUTO_TU_REGISTRAR( test_name )( \
boost::unit_test::make_test_case( \
&BOOST_AUTO_TC_INVOKER( test_name ), #test_name, \
MY_BOOST_TEST_ADD_ARGS \
(mbegin), (mend)) \
MY_BOOST_TEST_DEFAULT_DEC_COLLECTOR); \
\
void test_name::test_method(const param_t ¶m) \
#define BOOST_AUTO_PARAM_TEST_CASE( test_name, mbegin, mend ) \
BOOST_FIXTURE_PARAM_TEST_CASE( test_name, \
BOOST_AUTO_TEST_CASE_FIXTURE, \
mbegin, mend)
I took Omnifarious' header file and modified it such that the parameter is passed to the constructor of the test fixture rather than to the test method. This requires the test fixture's constructor declaration to take a single argument with the parameter's type. I found this to be super handy--much thanks for the initial question and answer!
#include <boost/test/unit_test_suite.hpp>
#include <boost/test/parameterized_test.hpp>
#include <type_traits>
#define BOOST_FIXTURE_PARAM_TEST_CASE( test_name, F, mbegin, mend ) \
struct test_name : public F { \
typedef ::std::remove_const< ::std::remove_reference< decltype(*(mbegin)) >::type>::type param_t; \
test_name(const param_t ¶m) : F(param) {} \
void test_method(void); \
}; \
\
void BOOST_AUTO_TC_INVOKER( test_name )(const test_name::param_t ¶m)\
{ \
test_name t(param); \
t.test_method(); \
} \
\
BOOST_AUTO_TU_REGISTRAR( test_name )( \
boost::unit_test::make_test_case( \
&BOOST_AUTO_TC_INVOKER( test_name ), #test_name, \
(mbegin), (mend))); \
\
void test_name::test_method(void) \
// *******
#define BOOST_AUTO_PARAM_TEST_CASE( test_name, mbegin, mend ) \
BOOST_FIXTURE_PARAM_TEST_CASE( test_name, \
BOOST_AUTO_TEST_CASE_FIXTURE, \
mbegin, mend)