Python -> C++ idiom: Storing lambda expressions in a map/container

boost, c++, idioms, lambda, qt

Solution

In C++ a lambda is nothing but syntactic sugar, that allows you to write functor definitions inline. A functor is an object with an `operator()`. So if you can't use C++11 (one reason might be because you're forced to use an old compiler?) then you can always just define functor classes, or if those conceptual lambdas don't capture anything, then you can just use plain old functions.

:-)

Kay, I'm going to write up an example. Just a few minutes...

Here goes…

struct QString{ QString( wchar_t const* ){} };
struct QVariant { QVariant( QString const& ) {} };
struct AType{ wchar_t const* name() const { return L""; } };

#include <functional>       // std::function
#include <map>              // std::map
#include <string>           // std::wstring
using namespace std;

void cpp11()
{
    typedef map< wstring, function< QVariant( AType const& ) > > Callbacks;
    Callbacks callbacks;
    callbacks[L"name"] = []( AType const& x )
        { return QVariant( QString( x.name() ) ); };

    auto const a = AType();
    auto variant = callbacks[L"name"]( a );
}

void cpp03Limited()
{
    typedef map< wstring, QVariant (*)( AType const& ) > Callbacks;
    Callbacks callbacks;

    struct SimpleConversion
    {
        static QVariant convert( AType const& x )
        { return QVariant( QString( x.name() ) ); }
    };

    callbacks[L"name"] = &SimpleConversion::convert;

    AType const a = AType();
    QVariant const variant = callbacks[L"name"]( a );
}

void cpp03General()
{
    struct IConversion
    {
        virtual QVariant convert( AType const& ) const = 0;
    };

    struct ConversionInvoker
    {
        IConversion const*  pConverter;

        QVariant operator()( AType const& x ) const
        {
            return pConverter->convert( x );
        }

        explicit ConversionInvoker( IConversion const* const p = 0 )
            : pConverter( p )
        {}
    };

    typedef map< wstring, ConversionInvoker > Callbacks;
    Callbacks callbacks;

    struct SimpleConversion: IConversion
    {
        virtual QVariant convert( AType const& x ) const
        { return QVariant( QString( x.name() ) ); }
    };
    SimpleConversion const simpleConversionFunc;

    callbacks[L"name"] = ConversionInvoker( &simpleConversionFunc );

    AType const a = AType();
    QVariant const variant = callbacks[L"name"]( a );
}

int main()
{}

Disclaimer: untested code (except that it compiles with msvc and mingw g++).

Problem

I'm learning C++ (via Qt4) leveraging my python/pyqt4 experience, and I cannot seem to grasp the proper idiom for storing lambda expressions into a container for use as callbacks. I have a struct with a bunch of fields. And I want to create a map of callbacks that can properly format the fields a specific way. Here is the python equivalent of what I want to do: ``` from PyQt4.QtCore import QVariant, QString class AType(object): def __init__(self): self.name = "FOO" self.attr2 = "BAR" self.attr3 = "BAZ" # ... callbacks = {} callbacks['name'] = lambda x: QVariant(QString(x.name)) callbacks['attr2'] = lambda x: QVariant(QString(x.attr2)) callbacks['attr3'] = lambda x: QVariant(QString(x.attr3)) a = AType() variant = callbacks['name'](a) print variant.toString() # PyQt4.QtCore.QString(u'FOO') ``` At first I found native lambdas in C++ and started trying it out, but then found that it is apparently a C++11 feature. Edit: I want to know if there is a pre C++11 approach before I start investigating whether I can introduce the flag into the build system for the project. Then I looked at boost solutions. My project is already using boost, so I thought it might be a solution. I see there are both `lambda` and `Phoenix` options. To show that I have at least tried to make this work, here is my embarrassing failure: ``` ## my_class.h ## #include <QVariant> #include <QMap> #include <boost/function.hpp> QMap< uint, boost::function<QVariant (AType&)> > callbacks; ## my_class.cpp ## #include <boost/lambda/lambda.hpp> #include <boost/bind/bind.hpp> #include "my_class.h" // I invite you to laugh at this callbacks[0] = boost::bind(&QVariant, boost::bind(&QString::fromStdString, boost::bind(&AType::name, _1))); ``` After I wrote that last line, I realized I am spinning my wheels and better just ask more experience C++ developers about the idiomatic approach to creating a map of lambda callbacks (compatable with Qt). My goal is to be able to take a known index and a known `AType` instance, and be able to return a proper format `QVariant` Update This is the solution I found to work, based on the accepted answer. Using C++98 compatible solution. ``` #include <QMap> #include <QVariant> #include <QString> #include <QDebug> struct AType { AType(); std::string name, attr2, attr3; }; AType::AType() { name = "FOO"; attr2 = "BAR"; attr3 = "BAZ"; } typedef QMap< QString, QVariant (*)( AType const& ) > Callbacks; struct ATypeFieldMapper { static QVariant name( AType const& x ) { return QVariant(QString::fromStdString(x.name)); } static QVariant attr2( AType const& x ) { return QVariant(QString::fromStdString(x.attr2)); } static QVariant attr3( AType const& x ) { return QVariant(QString::fromStdString(x.attr3)); } }; int main() { Callbacks callbacks; callbacks["name"] = &ATypeFieldMapper::name; callbacks["attr2"] = &ATypeFieldMapper::attr2; callbacks["attr3"] = &ATypeFieldMapper::attr3; AType a; qDebug() << callbacks["name"](a).toString(); qDebug() << callbacks["attr2"](a).toString(); qDebug() << callbacks["attr3"](a).toString(); } //"FOO" //"BAR" //"BAZ" ```

Original source