Home > C++0x > VC++16 has decltype in Beta 1 (so Linq to C++0x looks a little nicer)

VC++16 has decltype in Beta 1 (so Linq to C++0x looks a little nicer)

Updated 2011-10-31 – Major update based on boost range adaptors!!

A while ago I wrote up a little demo of using lambdas to implement convenient lazy list comprehension in C++0x, but at the time the VC++ compiler I was using lacked decltype, and I found a need for it.

I just tried out the Beta 1 that was released this week, and it has decltype, so here’s the updated sample:

#include <iostream>
#include <vector>
#include <sstream>

// Some helpers for working with decltype 
// (maybe these are in std:: somewhere?)
template <class T>
T *make_ptr() { return (T *)0; }

template <class T>
const T &make_ref() { return *(make_ptr<T>()); }

// so you don't need to obtain boost::lexical_cast!
template <class T>
std::string to_string(const T &v)
{
    std::ostringstream s;
    s << v;
    return s.str();
}

// Unary function that always returns true
template <class T>
struct always_true
{
    bool operator() (const T &) const { return true; }
};

// Unary function that returns its argument
template <class T>
struct pass_thru
{
    T operator() (const T &e) const { return e; }
};

template <class TElemFrom, 
          class TElemTo, 
          class TIterFrom, 
          class TSelector,
          class TPredicate>
class filter
{
    TIterFrom from_;
    TIterFrom end_;
    const TSelector &selector_;
    const TPredicate &predicate_;

public:
    filter(TIterFrom from, TIterFrom end, 
             const TSelector &selector, 
             const TPredicate &predicate)
        : from_(from), end_(end), 
          selector_(selector), 
          predicate_(predicate) {}

    class const_iterator
    {
        TIterFrom from_;
        TIterFrom end_;
        const TSelector &selector_;
        const TPredicate &predicate_;

        void locate()
        {
            while (!done())
            {
                if (predicate_(*from_))
                    return;

                ++from_;
            }
        }

        bool done() const { return (from_ == end_); }

    public:
        const_iterator(TIterFrom from, TIterFrom end, 
                       const TSelector &selector,
                       const TPredicate &predicate)
            : from_(from), end_(end), 
              selector_(selector),
              predicate_(predicate) { locate(); }

        TElemTo operator*() const { return selector_(*from_); }

        const_iterator operator++()
        {
            ++from_;
            locate();
            return *this;
        }

        bool operator==(const const_iterator &other) const
            { return done() && other.done(); }

        bool operator!=(const const_iterator &other) const
            { return !done() || !other.done(); }
    };

    typedef TElemFrom value_type;

    const_iterator begin() const 
        { return const_iterator(from_, end_, selector_, predicate_); }

    const_iterator end() const 
        { return const_iterator(end_, end_, selector_, predicate_); }

    template <class TSelector>
    filter<TElemTo, 
             decltype(make_ref<TSelector>()(make_ref<TElemTo>())),
             const_iterator, 
             TSelector,
             always_true<TElemTo> >
        select(const TSelector &selector)
    {
        return filter<TElemTo, 
            decltype(make_ref<TSelector>()(make_ref<TElemTo>())), 
                      const_iterator, 
                      TSelector, 
                      always_true<TElemTo> >
                    (begin(), 
                     end(), 
                     selector, 
                     always_true<TElemTo>());
    }

    template <class TPredicate>
    filter<TElemTo, 
              TElemTo,
             const_iterator, 
             pass_thru<TElemTo>, 
             TPredicate> 
        where(const TPredicate &predicate)
    {
        return filter<TElemTo, 
                        TElemTo,
                        const_iterator, 
                        pass_thru<TElemTo>, 
                        TPredicate>
                    (begin(),
                     end(),
                     pass_thru<TElemTo>(), 
                     predicate);
    }
};

template <class TCollFrom>
filter<typename TCollFrom::value_type, 
         typename TCollFrom::value_type,
         typename TCollFrom::const_iterator, 
         pass_thru<typename TCollFrom::value_type>,
         always_true<typename TCollFrom::value_type> >
    from(const TCollFrom &from)
{
    return filter<typename TCollFrom::value_type, 
                    typename TCollFrom::value_type, 
                    typename TCollFrom::const_iterator, 
                    pass_thru<typename TCollFrom::value_type>, 
                    always_true<typename TCollFrom::value_type> >
                (from.begin(), 
                 from.end(), 
                 pass_thru<typename TCollFrom::value_type>(), 
                 always_true<typename TCollFrom::value_type>());
}

int main(int argc, char* argv[])
{
    std::vector<int> vecInts;
    vecInts.push_back(5);
    vecInts.push_back(2);
    vecInts.push_back(9);

    auto filtered = from(vecInts)
        .where([] (int n) { return n > 3; })
        .select([] (int n) { return "\"" + to_string(n) + "\""; });

    for (auto i = filtered.begin(); i != filtered.end(); ++i)
        std::cout << *i << std::endl;

    return 0;
}

The main function is the pay-off, of course. I have a vector of ints, and I wrap it something that ends up stored in a variable called filtered, which I can then iterate through. The filtering (discarding anything not greater than 3) and transforming (int to quoted string) of the items happens on-the-fly during that iteration.

(And to clarify, this is different from the last time I posted it because select no longer requires any type parameters to be explicitly given – thanks to decltype).

Advertisements
Categories: C++0x Tags:
  1. williamwsmithiii
    May 22, 2009 at 5:41 pm

    Time to start the opensource project?

  2. Daniel Earwicker
    May 26, 2009 at 12:23 pm

    Thanks, but I’m not sure there’s a need:

    http://www.boost.org/doc/libs/1_39_0/libs/iterator/doc/index.html

    That boost library is essentially the same idea, first implemented nearly a decade ago by the god-like David Abrahams. “transform_iterator” does the same thing as “select”, “filter_iterator” the same as “where”.

    What makes my little example look nicer is (a) lambdas, which could just as easily be used with boost interator adaptors, and (b) the fact that I wrap containers (pairs of iterators or “ranges”) instead of individual iterators, which is less flexible but much more readable.

    But there is also this:

    http://www.boost.org/doc/libs/1_39_0/libs/range/doc/utility_class.html#iter_range

    Which captures the idea of a pair of iterators. So combine boost range and boost iterator adaptors, and you have a much more solid foundation for all this stuff. I was just using the raw language features and the more traditional concepts of the std library to demonstrate lambdas and communicate these ideas in .NET terms.

    Maybe if I get some time I’ll post a rewrite built on top of those boost libraries. Also I think it would be more “C++” to use an operator overload >> to show the iterators working like a pipe (similar to the way this is all done in F# as well).

  3. gramic
    March 11, 2010 at 6:04 pm

    Nice example! I noticed that the “return” keyword could be left out according to the latest standard drafts. If it works than your code in the where and select statements will be even simpler.

    • earwicker
      March 16, 2010 at 11:53 am

      Thanks, I’ll take a look at that – might be worth updating this post!

  4. Link
    September 29, 2011 at 10:31 pm

    Hi i tried this code in mingw gcc and it failed to compile:

    filename | line | error msg
    linq to c++.cpp | 108 |error: declaration of ‘class TSelector’|
    linq to c++.cpp | 39 |error: shadows template parm ‘class TSelector’|
    linq to c++.cpp | 127 |error: declaration of ‘class TPredicate’|
    linq to c++.cpp | 40 |error: shadows template parm ‘class TPredicate’|

    it compiled in visual studio 2010 but gave me some warnings:

    1>linq to c++.cpp(145): warning C4512: ‘filter’ : assignment operator could not be generated
    1> with
    1> [
    1> TElemFrom=int,
    1> TElemTo=int,
    1> TIterFrom=std::_Vector_const_iterator<std::_Vector_val<int,std::allocator>>,
    1> TSelector=pass_thru,
    1> TPredicate=always_true
    1> ]
    1> linq to c++.cpp(173) : see reference to class template instantiation ‘filter’ being compiled
    1> with
    1> [
    1> TElemFrom=int,
    1> TElemTo=int,
    1> TIterFrom=std::_Vector_const_iterator<std::_Vector_val<int,std::allocator>>,
    1> TSelector=pass_thru,
    1> TPredicate=always_true
    1> ]
    1>linq to c++.cpp(98): warning C4512: ‘filter::const_iterator’ : assignment operator could not be generated
    1> with
    1> [
    1> TElemFrom=int,
    1> TElemTo=int,
    1> TIterFrom=std::_Vector_const_iterator<std::_Vector_val<int,std::allocator>>,
    1> TSelector=pass_thru,
    1> TPredicate=always_true
    1> ]
    1> linq to c++.cpp(57) : see declaration of ‘filter::const_iterator’
    1> with
    1> [
    1> TElemFrom=int,
    1> TElemTo=int,
    1> TIterFrom=std::_Vector_const_iterator<std::_Vector_val<int,std::allocator>>,
    1> TSelector=pass_thru,
    1> TPredicate=always_true
    1> ]
    1> linq to c++.cpp(43) : see reference to class template instantiation ‘filter::const_iterator’ being compiled
    1> with
    1> [
    1> TElemFrom=int,
    1> TElemTo=int,
    1> TIterFrom=std::_Vector_const_iterator<std::_Vector_val<int,std::allocator>>,
    1> TSelector=pass_thru,
    1> TPredicate=always_true
    1> ]
    1> linq to c++.cpp(174) : see reference to class template instantiation ‘filter’ being compiled
    1> with
    1> [
    1> TElemFrom=int,
    1> TElemTo=int,
    1> TIterFrom=filter<int,int,std::_Vector_const_iterator<std::_Vector_val<int,std::allocator>>,pass_thru,always_true>::const_iterator,
    1> TSelector=pass_thru,
    1> TPredicate=`anonymous-namespace’::
    1> ]
    1>linq to c++.cpp(145): warning C4512: ‘filter’ : assignment operator could not be generated
    1> with
    1> [
    1> TElemFrom=int,
    1> TElemTo=int,
    1> TIterFrom=filter<int,int,std::_Vector_const_iterator<std::_Vector_val<int,std::allocator>>,pass_thru,always_true>::const_iterator,
    1> TSelector=pass_thru,
    1> TPredicate=`anonymous-namespace’::
    1> ]
    1>linq to c++.cpp(98): warning C4512: ‘filter::const_iterator’ : assignment operator could not be generated
    1> with
    1> [
    1> TElemFrom=int,
    1> TElemTo=int,
    1> TIterFrom=filter<int,int,std::_Vector_const_iterator<std::_Vector_val<int,std::allocator>>,pass_thru,always_true>::const_iterator,
    1> TSelector=pass_thru,
    1> TPredicate=`anonymous-namespace’::
    1> ]
    1> linq to c++.cpp(57) : see declaration of ‘filter::const_iterator’
    1> with
    1> [
    1> TElemFrom=int,
    1> TElemTo=int,
    1> TIterFrom=filter<int,int,std::_Vector_const_iterator<std::_Vector_val<int,std::allocator>>,pass_thru,always_true>::const_iterator,
    1> TSelector=pass_thru,
    1> TPredicate=`anonymous-namespace’::
    1> ]
    1> linq to c++.cpp(43) : see reference to class template instantiation ‘filter::const_iterator’ being compiled
    1> with
    1> [
    1> TElemFrom=int,
    1> TElemTo=int,
    1> TIterFrom=filter<int,int,std::_Vector_const_iterator<std::_Vector_val<int,std::allocator>>,pass_thru,always_true>::const_iterator,
    1> TSelector=pass_thru,
    1> TPredicate=`anonymous-namespace’::
    1> ]
    1> linq to c++.cpp(175) : see reference to class template instantiation ‘filter’ being compiled
    1> with
    1> [
    1> TElemFrom=int,
    1> TElemTo=std::basic_string<char,std::char_traits,std::allocator>,
    1> TIterFrom=filter<int,int,filter<int,int,std::_Vector_const_iterator<std::_Vector_val<int,std::allocator>>,pass_thru,always_true>::const_iterator,pass_thru,`anonymous-namespace’::>::const_iterator,
    1> TSelector=`anonymous-namespace’::,
    1> TPredicate=always_true
    1> ]
    1>linq to c++.cpp(145): warning C4512: ‘filter’ : assignment operator could not be generated
    1> with
    1> [
    1> TElemFrom=int,
    1> TElemTo=std::basic_string<char,std::char_traits,std::allocator>,
    1> TIterFrom=filter<int,int,filter<int,int,std::_Vector_const_iterator<std::_Vector_val<int,std::allocator>>,pass_thru,always_true>::const_iterator,pass_thru,`anonymous-namespace’::>::const_iterator,
    1> TSelector=`anonymous-namespace’::,
    1> TPredicate=always_true
    1> ]
    1>linq to c++.cpp(98): warning C4512: ‘filter::const_iterator’ : assignment operator could not be generated
    1> with
    1> [
    1> TElemFrom=int,
    1> TElemTo=std::basic_string<char,std::char_traits,std::allocator>,
    1> TIterFrom=filter<int,int,filter<int,int,std::_Vector_const_iterator<std::_Vector_val<int,std::allocator>>,pass_thru,always_true>::const_iterator,pass_thru,`anonymous-namespace’::>::const_iterator,
    1> TSelector=`anonymous-namespace’::,
    1> TPredicate=always_true
    1> ]
    1> linq to c++.cpp(57) : see declaration of ‘filter::const_iterator’
    1> with
    1> [
    1> TElemFrom=int,
    1> TElemTo=std::basic_string<char,std::char_traits,std::allocator>,
    1> TIterFrom=filter<int,int,filter<int,int,std::_Vector_const_iterator<std::_Vector_val<int,std::allocator>>,pass_thru,always_true>::const_iterator,pass_thru,`anonymous-namespace’::>::const_iterator,
    1> TSelector=`anonymous-namespace’::,
    1> TPredicate=always_true
    1> ]
    1> linq to c++.cpp(177) : see reference to class template instantiation ‘filter::const_iterator’ being compiled
    1> with
    1> [
    1> TElemFrom=int,
    1> TElemTo=std::basic_string<char,std::char_traits,std::allocator>,
    1> TIterFrom=filter<int,int,filter<int,int,std::_Vector_const_iterator<std::_Vector_val<int,std::allocator>>,pass_thru,always_true>::const_iterator,pass_thru,`anonymous-namespace’::>::const_iterator,
    1> TSelector=`anonymous-namespace’::,
    1> TPredicate=always_true
    1> ]

    Some time has gone by so have you improved the code ?
    To perhaps make it compatible with gcc and warning free in vs2010 ?

    • earwicker
      October 9, 2011 at 11:53 am

      I will when I get some time, haven’t looked at it for a while. I should really re-do it completely by basing it on relevant pieces of boost.

  5. December 23, 2011 at 5:58 pm

    I am just working on own C++11 LINQ-like library.
    Anyone can participate: http://code.google.com/p/boolinq/

  1. October 31, 2011 at 1:03 pm

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: