Template for traversing tree node using boost :: variant

Here is my project for tree traversal node:

struct Leaf1{};
struct Leaf2{};
struct Leaf3{};
struct Leaf4{};
struct Leaf5{};

typedef boost::variant< Leaf4, Leaf5 > Node3;
typedef boost::variant< Leaf2, Leaf3, Node3> Node2;
typedef boost::variant< Node2, Leaf1 > Node1;

class NodeVisitor: public boost::static_visitor<void>
{
public:
    template<class Node>
    void operator()(const Node& e) const
    {
        boost::apply_visitor( *this, e );
    }

    void operator()(const Leaf1& e) const{}
    void operator()(const Leaf2& e) const{}
    void operator()(const Leaf3& e) const{}
    void operator()(const Leaf4& e) const{}
    void operator()(const Leaf5& e) const{}
};

      

So, I recursively visit the nodes until I get to the leaf. The problem above is that I have to add a stub for the operater () for each sheet. You can see that I have five of these stubs above, but in practice there are many more. Can you suggest a way to template this stub?

+3


source to share


2 answers


SOLUTION 1: SFINAE based technique

This decision is based on the fact that not being able to replace template parameters during template generation does not cause a compilation error (a replacement error is not an error): instead, this template is simply ignored when overloading is resolved. So, with some trick, you can choose which overloads of a particular function template should be visible depending on the template arguments provided at creation time.

When this method is used, it is important to ensure that the discriminatory conditions that determine the visibility of each congestion are mutually exclusive , or ambiguity may arise.

To begin with, you need to define some metaphase of traits that will help you determine if a particular class is a leaf:

// Primary template
template<typename T> struct is_leaf<T> { static const bool value = false; };

// Specializations...
template<> struct is_leaf<Leaf1> { static const bool value = true; };
template<> struct is_leaf<Leaf2> { static const bool value = true; };
...

      

Then you can use std::enable_if

(or boost::enable_if

if you are working with C ++ 98) to select which overload of the call operator should be visible:

class NodeVisitor: public boost::static_visitor<void>
{
public:

    // Based on the fact that boost::variant<> defines a type list called
    // "types", but any other way of detecting whether we are dealing with
    // a variant is OK
    template<typename Node>
    typename std::enable_if<
        !is_same<typename Node::types, void>::value 
        >::type 
    operator()(const Node& e) const
    {
        boost::apply_visitor( *this, e );
    }

    // Based on the fact that leaf classes define a static constant value
    // called "isLeaf", but any other way of detecting whether we are dealing
    // with a leaf is OK
    template<typename Leaf>
    typename std::enable_if<is_leaf<Leaf>::value>::type 
    operator()(const Leaf& e) const
    {
        ...
    }
};

      




SOLUTION 2: overload method

If you are working in C ++ 98 and do not want to use boost::enable_if

as a replacement for std::enable_if

, an alternative approach is to use overload and unused argument resolution to distinguish between two helper function overloads.First of all, you define two dummy classes:

struct true_type { };
struct false_type { };

      

Then you'll re-create the meta-face is_leaf<>

, properly specializing in sheets:

// Primary template
template<typename T> struct is_leaf<T> { typedef false_type type; };

// Specializations...
template<> struct is_leaf<Leaf1> { typedef true_type type; };
template<> struct is_leaf<Leaf2> { typedef true_type type; };
...

      

Finally, you instantiate one of these dummy types to select the correct helper function overload process()

:

class NodeVisitor: public boost::static_visitor<void>
{
public:

    template<typename T>
    void operator()(const T& e) const
    {
        typedef typename is_leaf<T>::type helper;
        process(e, helper());
    }

    template<typename Node>
    void process(const Node& e, false_type) const
    {
        boost::apply_visitor(*this, e);
    }

    template<typename Leaf>
    void process(const Leaf& e, true_type) const
    {
        ...
    }
};

      

+3


source


Use an extra level of indirection for the class LeafN

, for example:

template <typename LeafType>
struct LeafHolder
{
  // has real instance of leaf..
};

      

Then override the type of your variant



typedef boost::variant< LeafHolder<Leaf4>, LeafHolder<Leaf5> > Node3;
typedef boost::variant< LeafHolder<Leaf2>, LeafHolder<Leaf3>, Node3> Node2;
typedef boost::variant< Node2, LeafHolder<Leaf1 > Node1;

      

Now your visitor can become:

class NodeVisitor: public boost::static_visitor<void>
{
public:
    template<class Node>
    void operator()(const Node& e) const
    {
        boost::apply_visitor( *this, e );
    }

    // single function to handle all leaves...
    template <typename LeafType>
    void operator()(const LeafHolder<LeafType>& e) const{}
};

      

+2


source







All Articles