function object for [] and *

Discussion in 'C++' started by Rares Vernica, Jun 23, 2008.

  1. Hello,

    I need help to replace the following code:

    int *idx; //...
    for (int i = 0; i < n; i++, ++out)
    out = in[idx];

    with something like:

    int *idx; //...
    std::transform(idx, idx + n, out, ...);

    I think the operator for transform should look something like:

    __gnu_cxx::compose1(in[], *idx)

    So, I think, I need a function object to apply the [] operator to "in",
    given an int and another one to apply the * operator to each element in
    "idx".

    Thanks in advance,
    Rares Vernica
     
    Rares Vernica, Jun 23, 2008
    #1
    1. Advertising

  2. Rares Vernica

    Guest

    On Jun 23, 2:22 pm, Rares Vernica <> wrote:
    > Hello,
    >
    > I need help to replace the following code:
    >
    >   int *idx; //...
    >   for (int i = 0; i < n; i++, ++out)
    >     out = in[idx];
    >
    > with something like:
    >
    >   int *idx; //...    
    >   std::transform(idx, idx + n, out, ...);
    >
    > I think the operator for transform should look something like:
    >
    >     __gnu_cxx::compose1(in[], *idx)
    >
    > So, I think, I need a function object to apply the [] operator to "in",
    > given an int and another one to apply the * operator to each element in
    > "idx".
    >

    Here's a first cut assuming the contents of in and out are doubles.
    If they're not, change accordingly...

    struct Functor
    {
    std::vector<double> const & m_in;

    // constructor
    Functor (std::vector<double> const & in) : m_in (in)
    {}

    // functor operator
    double operator () (int i) const
    {
    return in ;
    }
    }

    std::transform (idx, idx + n, out, Functor (in));

    Make sure out has enough room in out or use std::back_inserter (out).

    HTH
     
    , Jun 23, 2008
    #2
    1. Advertising

  3. Rares Vernica

    Noah Roberts Guest

    wrote:
    > On Jun 23, 2:22 pm, Rares Vernica <> wrote:
    >> Hello,
    >>
    >> I need help to replace the following code:
    >>
    >> int *idx; //...
    >> for (int i = 0; i < n; i++, ++out)
    >> out = in[idx];
    >>
    >> with something like:
    >>
    >> int *idx; //...
    >> std::transform(idx, idx + n, out, ...);
    >>
    >> I think the operator for transform should look something like:
    >>
    >> __gnu_cxx::compose1(in[], *idx)
    >>
    >> So, I think, I need a function object to apply the [] operator to "in",
    >> given an int and another one to apply the * operator to each element in
    >> "idx".
    >>

    > Here's a first cut assuming the contents of in and out are doubles.
    > If they're not, change accordingly...
    >
    > struct Functor
    > {
    > std::vector<double> const & m_in;
    >
    > // constructor
    > Functor (std::vector<double> const & in) : m_in (in)
    > {}
    >
    > // functor operator
    > double operator () (int i) const
    > {
    > return in ;
    > }
    > }
    >
    > std::transform (idx, idx + n, out, Functor (in));
    >
    > Make sure out has enough room in out or use std::back_inserter (out).
    >
    > HTH


    You might also try something like so (untested):

    std::transform(idx, idx+n, out, boost::bind(&in_type::eek:perator[], in, _1));
     
    Noah Roberts, Jun 23, 2008
    #3
  4. Rares Vernica

    Eric Pruneau Guest

    "Rares Vernica" <> a écrit dans le message de news:
    ...
    > Hello,
    >
    > I need help to replace the following code:
    >
    > int *idx; //...
    > for (int i = 0; i < n; i++, ++out)
    > out = in[idx];
    >
    > with something like:
    >
    > int *idx; //...
    > std::transform(idx, idx + n, out, ...);
    >
    > I think the operator for transform should look something like:
    >
    > __gnu_cxx::compose1(in[], *idx)
    >
    > So, I think, I need a function object to apply the [] operator to "in",
    > given an int and another one to apply the * operator to each element in
    > "idx".
    >
    > Thanks in advance,
    > Rares Vernica
    >



    here is a generic way to do what you want using only stl stuff.

    using namespace std;

    // here is the functor, I assume idx is always of type int, if not, replace
    it with a template parameter

    template<typename T_out, typename T_in>
    struct Get : public binary_function<int, T_in, T_out>
    {
    // binary_function is needed by bind2nd
    T_out operator()(int idx, T_in in) const
    {
    return in[idx];
    }
    };

    int main()
    {
    int idx[3] = {0,1,2};
    int in[3] = {5,4,3};
    int out[3];

    transform(idx, idx+3, out, bind2nd(Get<int,int*>(), &in[0]));

    cout << out[0] <<"\n" <<out[1] <<"\n" <<out[2] <<"\n";
    return 0;
    }

    this print:
    5
    4
    3

    ---------------------------

    Eric Pruneau
     
    Eric Pruneau, Jun 24, 2008
    #4
  5. Rares Vernica

    Noah Roberts Guest

    Eric Pruneau wrote:
    > "Rares Vernica" <> a écrit dans le message de news:
    > ...
    >> Hello,
    >>
    >> I need help to replace the following code:
    >>
    >> int *idx; //...
    >> for (int i = 0; i < n; i++, ++out)
    >> out = in[idx];
    >>
    >> with something like:
    >>
    >> int *idx; //...
    >> std::transform(idx, idx + n, out, ...);
    >>
    >> I think the operator for transform should look something like:
    >>
    >> __gnu_cxx::compose1(in[], *idx)
    >>
    >> So, I think, I need a function object to apply the [] operator to "in",
    >> given an int and another one to apply the * operator to each element in
    >> "idx".
    >>
    >> Thanks in advance,
    >> Rares Vernica
    >>

    >
    >
    > here is a generic way to do what you want using only stl stuff.


    That is good, but it should be kept in mind that boost's bind will be in
    the next standard library as part of the "stl". Unless you're being
    told not to use it I would definitely recommend getting it and learning
    how. It's power and simplicity (as long as you're not trying to read
    it) is not even remotely reached by the current library's binders.
     
    Noah Roberts, Jun 24, 2008
    #5
  6. Rares Vernica

    James Kanze Guest

    On Jun 23, 8:22 pm, Rares Vernica <> wrote:

    > I need help to replace the following code:


    > int *idx; //...
    > for (int i = 0; i < n; i++, ++out)
    > out = in[idx];


    > with something like:


    > int *idx; //...
    > std::transform(idx, idx + n, out, ...);


    Why? The first is simple and easily readable. The second is
    likely to be obfuscation, unless you can find a very good name
    for the functional object. Unless this is a "standard"
    transformation, occuring in many different places in your
    application, you're almost certainly better off sticking with
    the orginal loop.

    At least for now. I believe that the next version of the
    standard will have some sort of support for lambdas; using
    transform with a lambda function here would make a lot of sense.
    Moving the transformation itself out into a separate object
    doesn't, however, unless that object is generally usable and can
    be given a good name which reflects what it does. And trying to
    simulate lambda with variations of boost::bind is probably not
    going to result in anything readable.

    > I think the operator for transform should look something like:


    > __gnu_cxx::compose1(in[], *idx)


    In other words, it should be some obscure object that many C++
    programmers aren't familiar with, rather than straightforward
    code which anyone can understand.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Jun 25, 2008
    #6
  7. Rares Vernica

    James Kanze Guest

    On Jun 23, 10:34 pm, Noah Roberts <> wrote:
    > wrote:
    > > On Jun 23, 2:22 pm, Rares Vernica <> wrote:


    > >> I need help to replace the following code:


    > >> int *idx; //...
    > >> for (int i = 0; i < n; i++, ++out)
    > >> out = in[idx];


    > >> with something like:


    > >> int *idx; //...
    > >> std::transform(idx, idx + n, out, ...);


    > >> I think the operator for transform should look something like:


    > >> __gnu_cxx::compose1(in[], *idx)


    > >> So, I think, I need a function object to apply the []
    > >> operator to "in", given an int and another one to apply the
    > >> * operator to each element in "idx".


    > > Here's a first cut assuming the contents of in and out are
    > > doubles. If they're not, change accordingly...


    > > struct Functor
    > > {
    > > std::vector<double> const & m_in;


    > > // constructor
    > > Functor (std::vector<double> const & in) : m_in (in)
    > > {}


    > > // functor operator
    > > double operator () (int i) const
    > > {
    > > return in ;
    > > }
    > > }


    > > std::transform (idx, idx + n, out, Functor (in));


    > > Make sure out has enough room in out or use std::back_inserter (out).


    > You might also try something like so (untested):


    > std::transform(idx, idx+n, out, boost::bind(&in_type::eek:perator[], in, _1));


    Which won't work if in_type is a C style array (or a pointer).
    (Otherwise, a simple, direct application of boost::bind is still
    readable enough that I would consider it more or less
    acceptable. Although I don't see anything wrong with the
    original code, and I'd probably wait until we get lambda
    expressions to upgrade it.)

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Jun 25, 2008
    #7
  8. Rares Vernica

    James Kanze Guest

    On Jun 24, 5:57 pm, Noah Roberts <> wrote:
    > Eric Pruneau wrote:
    > > "Rares Vernica" <> a écrit dans le message de news:
    > > ...
    > >> I need help to replace the following code:


    > >> int *idx; //...
    > >> for (int i = 0; i < n; i++, ++out)
    > >> out = in[idx];


    > >> with something like:


    > >> int *idx; //...
    > >> std::transform(idx, idx + n, out, ...);


    > >> I think the operator for transform should look something like:


    > >> __gnu_cxx::compose1(in[], *idx)


    > >> So, I think, I need a function object to apply the []
    > >> operator to "in", given an int and another one to apply the
    > >> * operator to each element in "idx".


    > > here is a generic way to do what you want using only stl stuff.


    > That is good,


    Actually, it's mostly good for obfuscation, moving the
    transformation logic out of the function, and forcing the reader
    to look elsewhere to know what is going on.

    > but it should be kept in mind that boost's bind will be in the
    > next standard library as part of the "stl". Unless you're
    > being told not to use it I would definitely recommend getting
    > it and learning how. It's power and simplicity (as long as
    > you're not trying to read it) is not even remotely reached by
    > the current library's binders.


    Boost's bind definitely belongs in everyone's tool kit, but it
    has it's limits as well---if the operator[] in question is the
    built in version, for example, it won't work. The next version
    of the standard will also include some support for lambda
    expressions, which would be an even more general solution.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Jun 25, 2008
    #8
  9. Rares Vernica

    gpderetta Guest

    On Jun 25, 9:46 am, James Kanze <> wrote:
    > On Jun 24, 5:57 pm, Noah Roberts <> wrote:
    >
    >
    >
    > > Eric Pruneau wrote:
    > > > "Rares Vernica" <> a écrit dans le message de news:
    > > > ...
    > > >> I need help to replace the following code:
    > > >>  int *idx; //...
    > > >>  for (int i = 0; i < n; i++, ++out)
    > > >>    out = in[idx];
    > > >> with something like:
    > > >>  int *idx; //...
    > > >>  std::transform(idx, idx + n, out, ...);
    > > >> I think the operator for transform should look something like:
    > > >>    __gnu_cxx::compose1(in[], *idx)
    > > >> So, I think, I need a function object to apply the []
    > > >> operator to "in", given an int and another one to apply the
    > > >> * operator to each element in "idx".
    > > > here is a generic way to do what you want using only stl stuff.

    > > That is good,

    >
    > Actually, it's mostly good for obfuscation, moving the
    > transformation logic out of the function, and forcing the reader
    > to look elsewhere to know what is going on.
    >
    > > but it should be kept in mind that boost's bind will be in the
    > > next standard library as part of the "stl".  Unless you're
    > > being told not to use it I would definitely recommend getting
    > > it and learning how.  It's power and simplicity (as long as
    > > you're not trying to read it) is not even remotely reached by
    > > the current library's binders.

    >
    > Boost's bind definitely belongs in everyone's tool kit, but it
    > has it's limits as well---if the operator[] in question is the
    > built in version, for example, it won't work.  The next version
    > of the standard will also include some support for lambda
    > expressions, which would be an even more general solution.
    >


    It will also not work if operator[] has been overloaded for const and
    non const 'this' (as it is usually the case). You need an explicit
    (and ugly) cast then. The OP bind example also has a problem if used
    with any container but plain arrays: bind closes its arguments by copy
    so it will cause the container to be copied: use boost::ref or
    boost::cref.

    With boost lambda you can do:

    namespace ll = boost::lambda;
    std::transform(idx, idx+n, out, ll::ret<whatever>(ll::var(in)[_1]));

    where 'whatever' is the result type for operator[]. In this case the
    const qualified value_type of 'in' should be enough. For std::vector
    (and standard containers in general) and builtin arrays, you do not
    need 'ret<>':

    namespace ll= boost::lambda;

    std::vector<int> m = ...;
    std::vector<int> idx = ...;
    std::vector<int> out = ...;
    std::transform(idx.begin(), idx.end(), out.begin(), ll::var(m)
    [ll::_1]);

    Which doesn't look that ugly.

    --
    gpd
     
    gpderetta, Jun 25, 2008
    #9
  10. Rares Vernica

    James Kanze Guest

    On Jun 25, 12:20 pm, gpderetta <> wrote:
    > On Jun 25, 9:46 am, James Kanze <> wrote:


    [...]
    > > Boost's bind definitely belongs in everyone's tool kit, but it
    > > has it's limits as well---if the operator[] in question is the
    > > built in version, for example, it won't work. The next version
    > > of the standard will also include some support for lambda
    > > expressions, which would be an even more general solution.


    > It will also not work if operator[] has been overloaded for const and
    > non const 'this' (as it is usually the case). You need an explicit
    > (and ugly) cast then.


    Good point.

    > The OP bind example also has a problem if used
    > with any container but plain arrays: bind closes its arguments by copy
    > so it will cause the container to be copied: use boost::ref or
    > boost::cref.


    > With boost lambda you can do:


    > namespace ll = boost::lambda;
    > std::transform(idx, idx+n, out, ll::ret<whatever>(ll::var(in)[_1]));


    > where 'whatever' is the result type for operator[]. In this case the
    > const qualified value_type of 'in' should be enough. For std::vector
    > (and standard containers in general) and builtin arrays, you do not
    > need 'ret<>':


    > namespace ll= boost::lambda;
    >
    > std::vector<int> m = ...;
    > std::vector<int> idx = ...;
    > std::vector<int> out = ...;
    > std::transform(idx.begin(), idx.end(), out.begin(), ll::var(m)
    > [ll::_1]);


    > Which doesn't look that ugly.


    No. I didn't consider boost::lambda, because it seems that
    every time I try it on something real, I hit up against some
    limitations of it, and end up having to add extra qualifiers,
    etc., in order to make it work. Of course, your ll::var would
    qualify there, but in this case, I guess the rest of the
    expression is simple enough that it doesn't feel too bothersome.

    In general, I think real support for lambdas (at the language
    level) should open up a lot of possibilities along these lines.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Jun 25, 2008
    #10
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. James Vanns
    Replies:
    7
    Views:
    7,042
    Evan Carew
    Jan 21, 2004
  2. Giannis Papadopoulos

    Function pointer to void function and int function

    Giannis Papadopoulos, Sep 5, 2005, in forum: C Programming
    Replies:
    5
    Views:
    1,243
    Barry Schwarz
    Sep 5, 2005
  3. Faheem Mitha
    Replies:
    2
    Views:
    674
    Faheem Mitha
    Aug 26, 2006
  4. Eshrath Ali Khan
    Replies:
    1
    Views:
    106
    Joakim Braun
    Nov 9, 2004
  5. alex
    Replies:
    3
    Views:
    532
    Richard Cornford
    Dec 28, 2006
Loading...

Share This Page