Функция Boost и лямбда, чтобы обернуть функцию - PullRequest
1 голос
/ 06 октября 2011

Я хочу преобразовать этот простой код:

void setZComp(Imath::V3f& pt)
{
    pt.z = 0.0;
}

int myfunc()
{
    ...

    std::vector<Imath::V3f> vec(5,Imath::V3f(1.0,1.0,1.0));
    std::for_each(vec.begin(),vec.end(),boost::bind(&setZComp,_1));

    ...
}

к чему-то в этом роде, для того чтобы setZComp не был объявлен снаружи, а для какого-то встроенного объявления

int myfunc()
{
    ...

    boost::function<double(Imath::V3f&)> f = (boost::lambda::_1 ->* &Imath::V3f::z = 0.0) ;
    std::for_each(vec.begin(),vec.end(),boost::bind(&f,_1));

    ...
}

Я довольно новичок в Boost Bind и Lambda, и я не знаю, можно ли это как-то сделать. Очевидно, что приведенный выше код не работает.

Ответы [ 6 ]

2 голосов
/ 06 октября 2011

Если вы не можете использовать лямбду C ++ 11, тогда вы можете использовать boost::lambda::bind.
Так что в вашем случае что-то вроде следующего:

boost::lambda::bind(&Imath::V3f::z, boost::lambda::_1) = 0.0

Полный пример, так как я не знаю ваших внутренних органов:

struct S
{
    S():i(0){};
    int i;
};
int main()
{
    std::vector<S> vec;
    vec.push_back(S());

    std::for_each(vec.begin(), vec.end(), boost::lambda::bind(&S::i, boost::lambda::_1) = 5);
    std::cout << vec.front().i << std::endl; // outputs 5
    return 0
}
2 голосов
/ 06 октября 2011

Вы используете кувалду, чтобы сломать орех?Иногда я думаю, что проще использовать обычный цикл for и установить переменную в явном виде самостоятельно.Это делает код намного проще для чтения и обслуживания.

typedef std::vector<Imath::V3f> V3fVector;
V3fVector vec(5,Imath::V3f(1.0,1.0,1.0));

for (V3fVector::iterator i = vec.begin(), iEnd = vec.end(); iEnd != i; ++i)
    i->z = 0.0;

Насколько полезна связь с бустом, это также синтаксический беспорядок, делающий простой код нечитабельным.

1 голос
/ 06 октября 2011

Как объяснено в разделе Переменные-члены как цели :

Указатель на переменную-член на самом деле не функция, а первый аргумент для [boost::lambda::bind] функция, тем не менее, может быть указателем на переменную-член.Вызов такого выражения привязки возвращает ссылку на элемент данных.

Таким образом, чтобы создать лямбда-выражение, которое обращается к члену z, вы можете использовать:

boost::lambda::bind(&Imath::V3f::z, boost::lambda::_1)

Возвращенный объект сам может быть использован в других выражениях.Например,

boost::lambda::bind(&Imath::V3f::z, boost::lambda::_1) = 0.0

означает «получить ссылку double на z член первого аргумента (тип Imath::V3f&) и присвоить значение 0,0».

Youзатем можно использовать эту лямбду с Boost.Function и std::for_each:

boost::function<void(Imath::V3f&)> f = boost::lambda::bind(&Imath::V3f::z, boost::lambda::_1) = 0.0;
std::for_each(vec.begin(), vec.end(), f);

. Для справки приведем полный компилируемый пример:

#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <boost/function.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/lambda/lambda.hpp>

namespace Imath
{
class V3f
{
public:
    double x, y, z;

    V3f(double x_, double y_, double z_)
        : x(x_), y(y_), z(z_)
    {
    }

    friend std::ostream& operator<<(std::ostream& os, const V3f& pt) {
        return (os << '(' << pt.x << ", " << pt.y << ", " << pt.z << ')');
    }
};
}

int main()
{
    std::vector<Imath::V3f> vec(5, Imath::V3f(1.0, 1.0, 1.0));
    boost::function<void(Imath::V3f&)> f = boost::lambda::bind(&Imath::V3f::z, boost::lambda::_1) = 0.0;
    std::for_each(vec.begin(), vec.end(), f);

    std::vector<Imath::V3f>::iterator it, end = vec.end();
    for (it = vec.begin(); it != end; ++it) {
        std::cout << *it << std::endl;
    }

    return EXIT_SUCCESS;
}

Выходы:

(1, 1, 0)
(1, 1, 0)
(1, 1, 0)
(1, 1, 0)
(1, 1, 0)
1 голос
/ 06 октября 2011

Вы могли бы также взглянуть на boost :: phoenix.Я думаю, что это более полная реализация функционального программирования для c ++, чем лямбда-библиотека.

0 голосов
/ 09 октября 2011

Если вы хотите использовать boost :: lambda, я иногда нахожу более понятным объявление переменной «указатель на член» непосредственно перед строкой, содержащей лямбду, которая затем позволяет вам вместо этого использовать оператор -> *использования boost :: lambda :: bind.

Однако, как указал Алан, простой цикл здесь может быть самым простым решением.Используйте BOOST_FOREACH, чтобы сделать его еще проще.

Вот модифицированная версия примера реализации mkaes, в которой вместо bind используется оператор -> *, а также показано, как использовать BOOST_FOREACH в качестве альтернативы.

#include <iostream>
#include <vector>
#include <boost/lambda/lambda.hpp>
#include <boost/foreach.hpp>

// I like to provide alternate names for the boost::lambda placeholders
boost::lambda::placeholder1_type& arg1 = boost::lambda::_1 ;
boost::lambda::placeholder2_type& arg2 = boost::lambda::_2 ;
boost::lambda::placeholder3_type& arg3 = boost::lambda::_3 ;

struct S
{
    S():i(0){};
    int i;
};

int main()
{
    std::vector<S> vec;
    vec.push_back(S());

    // Define this pointer-to-member so we can
    //  use it in the lambda via the ->* operator
    int S::* i = &S::i ;

    std::for_each(vec.begin(), vec.end(), &arg1->*i = 5);
    std::cout << vec.front().i << std::endl; // outputs 5

    // Alternatively, just use a simple foreach loop
    BOOST_FOREACH( S & s, vec )
    {
        s.i = 6 ;
    }
    std::cout << vec.front().i << std::endl; // outputs 6

    return 0 ;
}
0 голосов
/ 06 октября 2011

Если у вас есть доступ к последней версии g++ с поддержкой C ++ 11 или MSVC 2010, вы можете сделать следующее:

int myfunc()
{
    ...

    std::for_each(vec.begin(),vec.end(),[](Imath::V3f& pt){ pt.z = 0.0; });

    ...
}
...