4

Given:

boost::variant<T1,T2,T3,...,TN>

Calculate the following at compile time:

max(sizeof(T1), sizeof(T2), sizeof(T3),... ,sizeof(TN))

I had no idea how to approach this, but this answer shed some light on how I might get started. Using the code in that answer with two types, T1 and T2, I could use the following in a source file to get the size of the larger object:

size_t largestSize = sizeof(largest<T1, T2>::type);

This is exactly what I'd like to do, but I need the largest template to work with more than two classes - specifically, it would need to check all types stored in a boost::variant object.

I know that boost::variant has a types typedef, which defines some sort of list of types in the variant. The problem is, I get totally lost when I try to wrap my head around all the boost::mpl stuff in the implementation. I don't intuitively understand what boost::variant::types is, and how I might be able to pass it into my own template that does something with it.

In my head, this is what the final implementation might look like:

typedef boost::variant<T1, T2, T3, T4> MyVariant;
size_t largestSize = sizeof(largest<MyVariant::types>::type);

Unfortunately, I have no idea how to go about implementing this version of largest.

I'm not sure if this is a reasonable approach, so I'm open to any other ways to accomplish this (maybe apply a boost::static_visitor to all types at compile time?).

Community
  • 1
  • 1
Matt K
  • 598
  • 5
  • 19

3 Answers3

7

Just ignore the mpl stuff. Start with:

template <class T> struct max_variant_sizeof;

template <class... Ts>
struct max_variant_sizeof<boost::variant<Ts...>> {
    static constexpr size_t value = variadic_max(sizeof(Ts)...);
};

Now max_variant_sizeof<MyVariant>::value will forward all the sizes of all the types to a function. All you need to do is write that variadic_max:

constexpr size_t variadic_max(size_t v) { return v; }

template <class... Args>
constexpr size_t variadic_max(size_t a, size_t b, Args... cs)
{
    return variadic_max(std::max(a, b), cs...);
}

Before C++14, std::max() isn't constexpr, so that can be replaced with:

    return variadic_max((a > b ? a : b), cs...);

One thing worth noting about:

maybe apply a boost::static_visitor to all types at compile time?

Visitation with a variant is a runtime operation - your visitor gets called with the type that the variant happens to be holding on to. It will not be called with all the types.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • I went with this solution, but during my testing I noticed that this won't compile with GCC 4.8.2 or clang 3.5.0 and older, while Wojciech's answer below compiles with GCC 4.7.3 and clang 3.0. I also noticed that neither solution compiles with MSVC19. None of this really matters in the context of the question, but it might be useful for someone stumbling across this answer. – Matt K Mar 30 '16 at 16:51
2

You could also change the code from the link you attached to:

template <class First, class... Args>
struct largest: largest<First, typename largest<Args...>::type> {
};

template<bool, typename T1, typename T2>
struct is_cond {
    typedef T1 type;
};

template<typename T1, typename T2>
struct is_cond<false, T1, T2> {
    typedef T2 type;
};

template<typename T1, typename T2>
struct largest<T1, T2> {
     typedef typename is_cond< (sizeof(T1)>sizeof(T2)), T1, T2>::type type;
};

Then the usage could look like:

cout << sizeof(largest<int, char, double>::type) << endl;

Edit:

To make it work with boost::variant as well as any other variadic args templated class just add another specialization:

template <template <class...> class Var, class... Args>
struct largest<Var<Args...>>: largest<Args...> { };

Then usage could look e.g. (with tuple, that can be successfully changed to boost::variant):

cout << sizeof(largest<tuple<int, char, double>>::type) << endl;
W.F.
  • 13,888
  • 2
  • 34
  • 81
1

I have using the boost::mpl library as a generic library for compile time code operations.

Some header code preparation:

#include <boost/mpl/vector.hpp>
#include <boost/mpl/max_element.hpp>
#include <boost/mpl/transform_view.hpp>
#include <boost/mpl/sizeof.hpp>

#include <boost/type_traits/alignment_of.hpp>

// alignof_ headers
#include <boost/mpl/size_t.hpp>
#include <boost/mpl/aux_/na_spec.hpp>
#include <boost/mpl/aux_/lambda_support.hpp>

// alignof mpl style implementation (namespace injection) the same way as the `mpl::sizeof_` did
namespace boost {
namespace mpl {
    template<
        typename BOOST_MPL_AUX_NA_PARAM(T)
    >
    struct alignof_
        : mpl::size_t< boost::alignment_of<T>::value >
    {
        BOOST_MPL_AUX_LAMBDA_SUPPORT(1, alignof_, (T))
    };

    BOOST_MPL_AUX_NA_SPEC_NO_ETI(1, alignof_)
}
}

'

Some helper macro:

// generates compilation error and shows real type name (and place of declaration in some cases) in an error message, useful for debugging boost::mpl recurrent types

// old C++ standard compatible
//#define UTILITY_TYPE_LOOKUP_BY_ERROR(type_name) \
//    (*(::utility::type_lookup<type_name >::type*)0).operator ,(*(::utility::dummy*)0)

// can be applied in a class, but requires `decltype` support
#define UTILITY_TYPE_LOOKUP_BY_ERROR(type_name) \
    typedef decltype((*(::utility::type_lookup<type_name >::type*)0).operator ,(*(::utility::dummy*)0)) _type_lookup_t

namespace utility
{
    struct dummy {};

    template <typename T>
    struct type_lookup
    {
        typedef T type;
    };
}

'

Usage example:

namespace mpl = bost::mpl;

typedef mpl::vector<T1, T2, T3, T4, T5> storage_types_t;

typedef typename mpl::deref<
    typename mpl::max_element<
        mpl::transform_view<storage_types_t, mpl::sizeof_<mpl::_1> >
    >::type
>::type max_size_t; // type has stored max sizeof(T1, T2, T3, T4, T5)

typedef typename mpl::deref<
    typename mpl::max_element<
        mpl::transform_view<storage_types_t, mpl::alignof_<mpl::_1> >
    >::type
>::type max_alignment_t; // type has stored max alignof(T1, T2, T3, T4, T5)

// testing on real values
UTILITY_TYPE_LOOKUP_BY_ERROR(max_size_t);
UTILITY_TYPE_LOOKUP_BY_ERROR(max_alignment_t);

'

Visual Studio 2015 error output:

error C2039: ',': is not a member of 'boost::mpl::size_t<**calculated max sizeof here**>'
error C2039: ',': is not a member of 'boost::mpl::size_t<**calculated max alignment here**>'
Andry
  • 2,273
  • 29
  • 28