Matching a Boost.Proto grammar to a type
Asked Answered
S

2

10

I'm trying to make a grammar in Boost.Proto that matches a vector type, but when I give it a terminal of that type, it doesn't match the grammar. The type definition looks like this:

template <typename T, unsigned D>
struct vector
{
    typedef T scalar;
    enum { size = D };

    scalar& operator[](unsigned i)
    {
        return m_components[i];
    }

    scalar const& operator[](unsigned i) const
    {
        return m_components[i];
    }

private:
    scalar m_components[size];
};

The grammar I'm trying to get to match looks something like this:

namespace proto = boost::proto;
using proto::_;
using proto::N;

struct test:
    proto::terminal<vector<_, N> >
{};

The match fails:

int main ()
{
    BOOST_MPL_ASSERT((proto::matches<proto::terminal<vector<float, 2> >::type, test>));
}

How do I make a grammar that matches a specific type?

EDIT:

It appears that proto::_ and proto::N is not being used as a wildcard in custom types. The code does compile with this grammar (the matches assertion passes):

struct test:
    proto::terminal<vector<float, 2> >
{};

But does not work when either one of the wildcards are in the type:

struct test:
    proto::terminal<vector<float, N> >
{};

Or:

struct test:
    proto::terminal<vector<_, 2> >
{};

So if I can't use the wildcards my own types, how can I test if the expression is a terminal containing a vector?

Seersucker answered 25/6, 2011 at 19:29 Comment(0)
M
6

Boost.Proto doesn't work with non-type template parameters. If you can, change your vector type to use integral type wrappers, like this:

template <typename T, typename D>
struct vector
{
    typedef T scalar;
    enum { size = D::value };

    scalar& operator[](unsigned i)
    {
        return m_components[i];
    }

    scalar const& operator[](unsigned i) const
    {
        return m_components[i];
    }

private:
    scalar m_components[size];
};

Then you should be able to match as follows:

int main ()
{
    BOOST_MPL_ASSERT((proto::matches<
        proto::terminal<vector<float, mpl::int_<2> > >::type, 
        proto::terminal<vector<_, _> > 
    >));
}

Hope that helps!

Malikamalin answered 18/7, 2011 at 7:36 Comment(0)
S
7

To compare the type within a terminal to a type, you can use type traits. I have some traits structs set up that evaluate to true if the given type is a vector:

template <typename T>
struct is_vector:
    boost::mpl::false_
{};


template <typename T, unsigned Size>
struct is_vector <dev::math::vector <T, Size> >:
    boost::mpl::true_
{};

And then you can put this in your grammar:

proto::and_<
    proto::terminal<_>,
    proto::if_<is_vector<proto::_value>()>
>

I've tried this approach before, but the reason it didn't work was because I forward declared vector<...> in the wrong namespace in the header the traits structs were in.

Seersucker answered 25/6, 2011 at 21:45 Comment(1)
Just trying to the the same on a custom multi-D array class. Wonder if it is better to adapt proto to my class, or my class to proto. I'm trying to follow the first path since i don't want to mess up a lot of already working stuff, but i'm not sureTatro
M
6

Boost.Proto doesn't work with non-type template parameters. If you can, change your vector type to use integral type wrappers, like this:

template <typename T, typename D>
struct vector
{
    typedef T scalar;
    enum { size = D::value };

    scalar& operator[](unsigned i)
    {
        return m_components[i];
    }

    scalar const& operator[](unsigned i) const
    {
        return m_components[i];
    }

private:
    scalar m_components[size];
};

Then you should be able to match as follows:

int main ()
{
    BOOST_MPL_ASSERT((proto::matches<
        proto::terminal<vector<float, mpl::int_<2> > >::type, 
        proto::terminal<vector<_, _> > 
    >));
}

Hope that helps!

Malikamalin answered 18/7, 2011 at 7:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.