Search code examples
c++templatesboosttemplate-meta-programmingboost-mpl

Convert an mpl sequence of sequences into a trie


The problem looks simple enough, basically I have a sequence of sequences, something like:

typedef mpl::vector<
  mpl::vector<mpl::_1, mpl::_2>,
  mpl::vector<mpl::_1, mpl::_2, mpl::_3>,
  mpl::vector<mpl::_2, mpl::_1>,
  mpl::vector<mpl::_2, mpl::_2>,
  mpl::vector<mpl::_2, mpl::_2, mpl::_3>
> seq;

What I would like to do is to transform this to a trie, the end result being something like:

mpl::map<
  mpl::pair<mpl::_1, 
    mpl::map<
      mpl::pair<mpl::_2,
        mpl::map<
          mpl::pair<TERMINAL, T>,
          mpl::pair<mpl::_3,
            mpl::map<
              mpl::pair<TERMINAL, T>
            >
          >
        >
      >
    >
  >
  mpl::pair<mpl::_2, 
    mpl::map<
      mpl::pair<mpl::_1,
        mpl::map<
          mpl::pair<TERMINAL, T>
        >
      >,
      mpl::pair<mpl::_2,
        mpl::map<
          mpl::pair<TERMINAL, T>,
          mpl::pair<mpl::_3,
            mpl::map<
              mpl::pair<TERMINAL, T>
            >
          >
        >
      >
    >
  >
>

So, the question is, is this possible (I'm thinking it's not)? If it is possible, which dark spells have I missed?

EDIT: In case the above transformation from sequence of sequences to a trie is not clear, let me see if I can state it in plain English (often more difficult.) Basically each sequence within the main sequence is composed of some types (_1, _2 etc.) The transformed version is trie where common prefixes are collapsed. May be the attached picture helps..

resulting tree

EDIT2: Thanks to @Yakk, hopefully now the question is clearer...


Solution

  • There you go:

    struct Terminal;
    
    template < typename Trie, typename First, typename Last,
               typename Enable = void >
    struct insertInTrie_impl
    {
        typedef typename
            mpl::deref<First>::type key;
    
        typedef typename 
            mpl::at<
                Trie,
                key
            >::type subTrieOrVoid; // would be easier if "at" supported Default
    
        typedef typename
            mpl::if_<
                boost::is_same< subTrieOrVoid, mpl::void_ >,
                mpl::map<>,
                subTrieOrVoid
            >::type subTrie;
    
        typedef typename
            mpl::insert<
                Trie,
                mpl::pair<
                    key, typename
                    insertInTrie_impl<
                        subTrie, typename
                        mpl::next<First>::type,
                        Last
                    >::type
                >
            >::type type;
    };
    
    template < typename Trie, typename First, typename Last >
    struct insertInTrie_impl< Trie, First, Last, typename 
        boost::enable_if< boost::is_same<First, Last> >::type >
        : mpl::insert<
            Trie,
            mpl::pair< Terminal, Terminal >
            // I'm not sure what you want in your terminal node
        >
    {};
    
    template < typename Trie, typename Seq >
    struct insertInTrie
        : insertInTrie_impl< 
            Trie, typename 
            mpl::begin<Seq>::type, typename 
            mpl::end<Seq>::type
        >
    {};
    
    
    template < typename SeqOfSeq >
    struct constructTrie
        : mpl::fold< 
            SeqOfSeq,
            mpl::map<>,
            insertInTrie< mpl::_1, mpl::_2 >
        >
    {};
    

    insertInTrie_impl is a recursive metafunction that inserts a sequence into an existing trie, using iterators. insertInTrie accepts a sequence an calls insertInTrie_impl. constructTrie applies insertInTrie to all sequences in the given sequence, starting with an empty trie.

    In pseudo-code, this reads as follow:

    Trie insertInTrie_impl(trie, first, last)
    {
        if (first == last)
        {
            trie.insert(Terminal, Terminal);
            return trie;
        }
    
        key = *first;
    
        subTrie = trie[key];
        if (subTrie = void) // key not found
        {
            subTrie = emptyTrie;
        }
    
        trie.insert(key, insertInTrie_impl(subTrie, ++first, last))
    
        return trie;
    }
    
    Trie insertInTrie(trie, seq)
    {
        return insertInTrie_impl(trie, seq.begin(), seq.end();
    }
    
    Trie constructTrie(seqOfSeq)
    {
        return fold(seqOfSeq, emptyTrie, insertInTrie);
    }
    

    Finally, a sample use:

    int main()
    {
        typedef mpl::vector<
            mpl::vector<mpl::_1, mpl::_2>,
            mpl::vector<mpl::_1, mpl::_2, mpl::_3>,
            mpl::vector<mpl::_2, mpl::_1>,
            mpl::vector<mpl::_2, mpl::_2>,
            mpl::vector<mpl::_2, mpl::_2, mpl::_3>
        > seqOfSeq;
    
        typedef constructTrie< seqOfSeq >::type bigTrie;
    
        BOOST_MPL_ASSERT(( 
            mpl::has_key<
                mpl::at< 
                    mpl::at< 
                        bigTrie, 
                        mpl::_1
                    >::type, 
                    mpl::_2
                >::type, 
                Terminal
            > ));
        BOOST_MPL_ASSERT(( 
            mpl::has_key<
                mpl::at< 
                    mpl::at< 
                        mpl::at< 
                            bigTrie, 
                            mpl::_1
                        >::type,
                        mpl::_2
                    >::type, 
                    mpl::_3
                >::type, 
                Terminal
            > ));
        BOOST_MPL_ASSERT(( 
            mpl::has_key<
                mpl::at< 
                    mpl::at< 
                        bigTrie, 
                        mpl::_2
                    >::type,
                    mpl::_2
                >::type, 
                Terminal
            > ));
    }