Skip to content

Instantly share code, notes, and snippets.

@scraimer
Last active June 14, 2017 05:15
Show Gist options
  • Save scraimer/2d954a89a1345d15736cade249878509 to your computer and use it in GitHub Desktop.
Save scraimer/2d954a89a1345d15736cade249878509 to your computer and use it in GitHub Desktop.
Get member if present, or default value if not
/*
* Goal: To have a class template that can recieve a template parameter of a class that either
has a member (e.g. _time) or doesn't have a member.
The class template has a function (e.g. get_time) which should return the value (e.g. _time),
or a default value (e.g. 22)
So far, I've got a class template 'getter_maker_t' that depending on its arguments can
will either create a class that returns the value, or returns a class that returns the default value.
TODO:
- Have a single expression for both case: when the class member is present, and when it is not
- Move it all into a namespace
*/
#include <boost/convert/detail/has_member.hpp>
#include <boost/utility/enable_if.hpp>
#include <stdint.h>
#include <boost/type_traits/conditional.hpp>
#include <boost/type_traits/integral_constant.hpp>
template <typename T>
class message
{
public:
T _header; // just public so I can set the value of _header._time in main.
};
class reuters_header
{
public:
uint64_t _time;
};
class ebsu_header
{
public:
// no time!
};
BOOST_DECLARE_HAS_MEMBER(has_time, _time);
template <typename HEADER_T, typename MEMBER_T, MEMBER_T HEADER_T::*member_offset_ptr>
class ref_to_value
{
public:
MEMBER_T const & _value;
ref_to_value( HEADER_T const & header ) : _value(header.*member_offset_ptr) { }
};
template <typename HEADER_T, typename MEMBER_T, static MEMBER_T const & default_value>
class ref_to_default
{
public:
MEMBER_T const & _value;
ref_to_default( HEADER_T const & header ) : _value(default_value) { }
};
template <typename HEADER_T, typename MEMBER_T, static MEMBER_T const & default_value, MEMBER_T HEADER_T::*member_offset_ptr = nullptr, bool enable = false>
class getter_t
{
public:
typedef typename boost::conditional<enable, ref_to_value<HEADER_T, MEMBER_T, member_offset_ptr>, ref_to_default<HEADER_T, MEMBER_T, default_value> >::type ref_t;
ref_t _ref;
getter_t( HEADER_T const & header ) : _ref(header) { }
MEMBER_T const & get_value() { return _ref._value; }
};
template <typename HEADER_T, typename MEMBER_T, static MEMBER_T const & default_value, MEMBER_T HEADER_T::*member_offset_ptr = nullptr>
class getter_maker_t : public getter_t<HEADER_T, MEMBER_T, default_value, member_offset_ptr, has_time<HEADER_T>::value>
{
public:
getter_maker_t( HEADER_T const & header ) : getter_t<HEADER_T, MEMBER_T, default_value, member_offset_ptr, has_time<HEADER_T>::value>(header) { }
};
////
template <typename HEADER_T, typename MEMBER_T, MEMBER_T HEADER_T::*ptr_to_member, typename enable = void>
struct get_ptr_to_member_or_null
{
constexpr BOOST_STATIC_CONSTANT(auto, value = ptr_to_member);
};
template <typename HEADER_T, typename MEMBER_T, MEMBER_T HEADER_T::*ptr_to_member>
struct get_ptr_to_member_or_null<HEADER_T, MEMBER_T, ptr_to_member, boost::enable_if<boost::true_type>::type >
{
constexpr BOOST_STATIC_CONSTANT(auto, value = nullptr);
};
// Before C++11, 'cannot use local types as template parameters'. So this is defined globally.
static uint64_t const default_value_for_missing_time = 22;
int main()
{
message<reuters_header> rm;
rm._header._time = 33;
message<ebsu_header> em;
////
// Iter #1: very different classes: ref_to_value, ref_to_default
ref_to_value<reuters_header, decltype(reuters_header::_time), &reuters_header::_time> gm( rm._header );
auto a = gm._value;
ref_to_default<ebsu_header, uint64_t, default_value_for_missing_time> ge( em._header );
auto b = ge._value;
////
// Iter #2: same class, getter_t, but with very different parameters
getter_t<reuters_header, uint64_t, default_value_for_missing_time, &reuters_header::_time, true> g1( rm._header );
auto c = g1.get_value();
getter_t<ebsu_header, uint64_t, default_value_for_missing_time> g2( em._header);
auto d = g2.get_value();
//// Iter #3: same class, with fewer and nearly the same parameters
getter_maker_t<reuters_header, uint64_t, default_value_for_missing_time, &reuters_header::_time> gm1( rm._header );
auto e = gm1.get_value();
getter_maker_t<ebsu_header, uint64_t, default_value_for_missing_time, nullptr> gm2( em._header );
auto f = gm2.get_value();
///
getter_maker_t<reuters_header, uint64_t, default_value_for_missing_time,
get_ptr_to_member_or_null<reuters_header, uint64_t, &reuters_header::_time>::value
> gm10( rm._header );
auto g = gm10.get_value();
getter_maker_t<ebsu_header, uint64_t, default_value_for_missing_time,
get_ptr_to_member_or_null<ebsu_header, uint64_t, &ebsu_header::_time>::value
> gm11( em._header );
auto h = gm11.get_value();
return g + h;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment