0

I have a Variant type which I want to use in something like a JSON parser. A type in JSON can include objects and arrays. These objects and arrays can contain members of their own type:

typedef variant<int,float,bool,std::string> baseType;
typedef std::vector<baseType> arrayType; // problem, can't have arrays of arrays
typedef std::unordered_map<std::string,baseType> objType; // problem, can't have objects or arrays within objects.

How do I create a "recursive" templated type?

Something like:

typedef variant<int,float,bool,std::string,std::vector<type>,std::unordered_map<std::string,type> type;

I'm aware of boost; I'm not interested in answers that use it. I've tried to work out how it does it with recursive_wrapper but the liberal use of pre-processor macros is making it very hard for me.

EDIT

With the help of Yakk below, I've gotten this far:

template<typename... Ts>
class Variant;

template<class T>
struct tag
{
    using type=T;
};

template<class X, class A, class B>
struct subst : tag<X>
{};

template<class X, class A, class B>
using subst_t = typename subst<X,A,B>::type;

template<class A, class B>
struct subst<A,A,B> : tag<B>
{};

template<class X, class A, class B>
struct subst<X&,A,B> : tag<subst_t<X,A,B>&>
{};

template<class X, class A, class B>
struct subst<X&&,A,B> : tag<subst_t<X,A,B>&&>
{};

template<class X, class A, class B>
struct subst<X const,A,B> : tag<subst_t<X,A,B>const>
{};

template<class X, class A, class B>
struct subst<X volatile,A,B> : tag<subst_t<X,A,B>volatile>
{};

template<class X, class A, class B>
struct subst<X const volatile,A,B> : tag<subst_t<X,A,B>const volatile>
{};

template<template<class...> class Z, class... Xs, class A, class B>
struct subst<Z<Xs...>,A,B> : tag<Z<subst_t<Xs,A,B>...>>
{};

template<template<class,size_t> class Z, class X, size_t n, class A, class B>
struct subst<Z<X,n>,A,B> : tag<Z<subst_t<X,A,B>,n>>
{};

template<class R, class...Xs, class A, class B>
struct subst<R(Xs...),A,B> : tag<subst_t<R,A,B>(subst_t<Xs,A,B>...)>
{};

struct RecursiveType {};

template<typename Sig>
struct RecursiveVariant
{
    using VariantType = Variant<subst_t<Sig,RecursiveType,RecursiveVariant>>;

    template<typename V,
        typename std::enable_if<
            !std::is_same<RecursiveVariant,typename std::decay<V>::type>::value
            && std::is_convertible<V,VariantType>::value
        >
    ::type>
    RecursiveVariant(V&& vIn)
    :
        m_variant(vIn)
    {}

    RecursiveVariant(){};


    template<typename T, typename... Args>
    void Set(Args&&... args)
    {
        m_variant.Set<T,Args...>(std::forward<Args>(args)...);
    }

    template<typename T>
    const T& Get() const
    {
        return m_variant.Get<T>();
    }

    template<typename T>
    T& Get()
    {
        return m_variant.Get<T>();
    }

    VariantType m_variant;
};

The actual Variant type I have is here https://codereview.stackexchange.com/questions/127372/variant-class-that-i-dont-think-is-missing-anything

I think the above RecursiveWrapper is way off. As I understand it, I should use it as the actual type, i.e.

RecursiveWrapper<RecursiveType> Variant

However, this can't be correct because nowhere have I specified the types allowed in Variant. Questions about the code I don't understand are

  1. What is RecursiveType?
  2. How can I forward variadic template types to the underlying Variant?
Community
  • 1
  • 1
NeomerArcana
  • 1,978
  • 3
  • 23
  • 50
  • It seems like you're confusing templates with containers. An "array of arrays" would look like `std::vector >`. That would be an array of arrays of strings. Could you elaborate on why `variant` in your question is templated like it is? – Jfevold May 13 '16 at 21:53
  • Sorry, I thought that was obvious. `variant` is templated because it takes as template params, the types that it is allowed to hold. In order for it to hold a container of itself, the typedef needs to be able to reference itself so that the container types, `vector` and `unordered_map`, are able to contain the same type. That's probably as clear as mud. – NeomerArcana May 13 '16 at 21:59
  • I still don't understand why you would template that. Is there a situation in which you would want the same code to work for a variant of other types? – Jfevold May 13 '16 at 22:01
  • Yes. The alternative would be to create a class hierarchy and pointer types every time I needed a new combination variant. – NeomerArcana May 13 '16 at 22:08
  • Are you actually trying to write a JSON parser, or are you just hoping to use one to do something else? If the latter, there are well-tested libraries like Jansson for parsing JSON (http://www.digip.org/jansson/). They might not be exactly what you want, but you'll probably save time overall. – Ben S. May 13 '16 at 22:52
  • Is [this](http://stackoverflow.com/a/29533782/1774667) recursive function definition helpful? Should be adaptable. However, vectors or unordered meows of incomplete type may not be a wrll formed program (maybe there is a defect or proposal about that issue? No reasonable practical reason for restriction, but it existed las time I checked) – Yakk - Adam Nevraumont May 13 '16 at 23:00
  • @Yakk it looks like it's along the lines of what I want. But I'm not sure how I go about using the substitution templates themselves. – NeomerArcana May 14 '16 at 10:22
  • `using func=std::function< subst_t >;` is where the linked one does it. Create a non-recursive implementation that can handle incomplete types (tricky), in the function example it is `std::function`. In recursive wrapper do the subst like above. – Yakk - Adam Nevraumont May 14 '16 at 18:36
  • @Yakk, I'm afraid a bit of that went over my head. I've updated my question, could you show me exactly how to use the template type substitution? – NeomerArcana May 15 '16 at 03:56
  • No. Go use boost variant (which has other issues), or read some implementation of `std::experimental::variant`. I'd find solving the problem tricky to get right, and I don't have hours of time right now. There is a reason why I labelled a step as (tricky). Or use a heap-based variant (basically an `any`), those are easy. – Yakk - Adam Nevraumont May 15 '16 at 04:03

1 Answers1

-1

I would use boost variant, in particular see http://www.boost.org/doc/libs/1_61_0/doc/html/boost/make_recursive_variant.html and http://www.boost.org/doc/libs/1_61_0/doc/html/variant/tutorial.html#variant.tutorial.recursive.recursive-variant

Mircea Baja
  • 464
  • 4
  • 14