Toon posts:

[C++] functor calls.. std::transform wrappen

Pagina: 1
Acties:

Onderwerpen


  • TheWickedD
  • Registratie: Juli 2002
  • Laatst online: 11-03 21:01
Beste Tweakers,

Ik gebruik best wel eens std::vectors waarbij ik de inhoud van een type naar een ander moet converteren.

Ik dacht dit eens generiek op te lossen door een std::transform oplossing te wrappen:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// standard caster functions
template <typename T>
struct static_cast_func
{
    template <typename T1>
    T operator()(const T1& x) const
    {
        return static_cast<T>(x);
    }
};

// converts elements of type T1 in input vector to type T2 and put in output vector
// conversion by default happens with a functor wrapping static cast, but user can
// provide his own functor if needed
template <class T1, class T2, class F1>
inline void ConvertSeries (const std::vector<T1> is, std::vector<T2>& os, F1 func)
{
    os.clear();
    std::transform(is.begin(), is.end(), std::back_inserter(os), func());
}

// geen default template arguments bij functies, dus dan maar zo
// (weet niet of ik wel een default argument voor func hierboven zou
// kunnen opgeven, compiler zou daar het type van F1 uit moeten
// kunnen halen, maar met de andere errors waar ik tegenaan loop
// kan ik dit niet testen)
template <class T1, class T2>
inline void ConvertSeries (const std::vector<T1> is, std::vector<T2>& os)
{
    ConvertSeries(is, os, static_cast_func<T2>());
}

wat ik dan bijvoorbeeld zo dacht aan te roepen:
C++:
1
2
3
4
5
ConvertSeries(bgColor_, bgColor, static_cast_func<float>());
// of, aangezien dit de standaard functor is:
ConvertSeries(bgColor_, bgColor);
// vergelijk met:
std::transform(bgColor_.begin(), bgColor_.end(), std::back_inserter(bgColor), static_cast_func<float>());


Dit compiled niet met de melding: "error C2780: 'float static_cast_func<T>::operator ()(const T1 &) const' : expects 1 arguments - 0 provided"

Met een beetje testen denk ik dat dit komt omdat de functie niet juist geinlined kan worden (als ik de body van de eerste ConvertSeries simpelweg return func(1.) (en het return type natuurlijk T2 maak), dan compileerd het prima). Vervelend.

Maargoed, hoe zouden jullie mijn probleem oplossen? Het gaat mij er vooral om dat ik makkelijk kan converteren, gebruik makend van een default functor of een user-provided.

Bedankt voor het meedenken!

  • xos
  • Registratie: Januari 2002
  • Laatst online: 12-05 11:32
Op regel 19 probeer je de () operator aan te roepen van F1 met 0 argumenten terwijl je een instantie wilt meegeven. Verander dat eens naar:

C++:
1
2
3
4
5
6
template <class T1, class T2, class F1> 
inline void ConvertSeries (const std::vector<T1> is, std::vector<T2>& os, F1) 
{ 
    os.clear(); 
    std::transform(is.begin(), is.end(), std::back_inserter(os), F1()); 
}

  • TheWickedD
  • Registratie: Juli 2002
  • Laatst online: 11-03 21:01
Bedankt, dat compiled inderdaad, bedankt!

Maar nu werdt er een nieuwe instantie gemaakt in ConvertSeries, toch?
wat test code:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
#include <vector>

// standard caster function
template <typename T>
struct static_cast_func
{
    template <typename T1>
    T operator()(const T1& x) const
    {
        return static_cast<T>(x);
    }
};

// converts elements of type T1 in input vector to type T2 and put in output vector
// conversion by default happens with a functor wrapping static cast, but user can
// provide his own functor if needed
template <class T1, class T2, class F1>
inline void ConvertSeries (const std::vector<T1> is, std::vector<T2>& os, F1 func)
{
    std::cout << &F1() << std::endl;
    os.clear();
    std::transform(is.begin(), is.end(), std::back_inserter(os), func);
}

template <class T1, class T2>
void ConvertSeries (const std::vector<T1> is, std::vector<T2>& os)
{
    static_cast_func<T2> st;
    std::cout << &st << std::endl;
    ConvertSeries(is, os, st);
}


int main()
{
    std::vector<float>  bgColorf;
    std::vector<double> bgColord;

    bgColord.push_back(.4);
    bgColord.push_back(.5);
    bgColord.push_back(.6);
    bgColord.push_back(.7);

    ConvertSeries(bgColord,bgColorf);

    return 0;
}


de twee cout's geven mij verschillende adressen terug. Nu is dit in mijn geval geen probleem, maar als ik een keer een functor met interne state nodig heb is dit wel een probleem. Ik heb het daarom zoals je in het code fragment ziet net iets anders opgelost met het stukje inspiratie dat je me gaf. Is dit een nette oplossing?
Bedankt!

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03 21:53

MLM

aka Zolo

Dat kan, maar je zit dan wel met kopietjes van F1 te werken (tis immers een pass by value), dus als je dure objecten gaat rondgooien met interne state, wil je misschien (const) references gebruiken.

(Daarnaast, is het intended dat je "is" argument geen reference is?)

-niks-


Acties:
  • 0Henk 'm!

  • TheWickedD
  • Registratie: Juli 2002
  • Laatst online: 11-03 21:01
Hoi MLM,

Je hebt gelijk, d'r missen een paar ampersands, ik wil alleen references gebruiken hier om niets te kopieren.

Bedankt!

Acties:
  • 0Henk 'm!

  • TheWickedD
  • Registratie: Juli 2002
  • Laatst online: 11-03 21:01
Ok, fyi, heb er nu dit van gemaakt aangezien ik 99% van de tijd dit soort gedrag wil zien. Hoop dat iemand er wat aan heeft.

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <vector>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits.hpp>

// Like python, output is created by applying provided functor to each element
// in the input vector (just a convenience wrapper around std::transform)
template <class T1, class T2, class F1>
inline void MapVector(const std::vector<T1>& is, std::vector<T2>& os, F1& func)
{
    os.clear();
    std::transform(is.begin(), is.end(), std::back_inserter(os), func);
}

// standard caster function
template <typename T>
struct static_cast_func
{
    template <typename T1>
    typename boost::enable_if   // special case if converting from floating point type to integral type: round to nearest integer
    <
        boost::type_traits::ice_and
        <
            boost::is_integral<T>::value,
            boost::is_floating_point<T1>::value
        >
    , T>::type        // integral types
    operator()(const T1& x) const
    {
        return static_cast<T>(x+T1(.5));
    }

    template <typename T1>
    typename boost::disable_if   // all other cases
    <
        boost::type_traits::ice_and
        <
            boost::is_integral<T>::value,
            boost::is_floating_point<T1>::value
        >
    , T>::type
    operator()(const T1& x) const
    {
        return static_cast<T>(x);
    }
};

// converts elements of type T1 in input vector to type T2 and put in output vector
// conversion by default happens with a functor wrapping static cast, but user can
// provide his own functor if needed (use MapVector in that case)
template <class T1, class T2>
inline void ConvertSeries(const std::vector<T1>& is, std::vector<T2>& os)
{
    MapVector(is, os, static_cast_func<T2>());
}

Acties:
  • 0Henk 'm!

  • xos
  • Registratie: Januari 2002
  • Laatst online: 12-05 11:32
Persoonlijk zie ik weinig voordeel boven een direct aanroep naar transform. Transform werkt op input/output iterators en dus alle containers die deze types ondersteunen (eventueel via een wrapper als een back_inserter). Zo complex is dat toch niet om dat in functies weg te abstractheren?

Eigenlijk zou ik verwachten dat dit ook niet compileert omdat op regel 53 een temporary wordt gemaakt die aan een non const reference parameter wordt doorgegeven. Het staat de stl implementators trouwens vrij om te doen en laten met functors wat ze willen. Maw, in stl zal de functor mogelijk ook by value worden doorgegeven. Dus ik zou er persoonlijk niet zo zwaar aan tillen om de functor by value door te geven.

[Voor 21% gewijzigd door xos op 03-07-2011 10:16]


Acties:
  • 0Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 13:20

.oisyn

Moderator Devschuur® / Cryptocurrencies

Demotivational Speaker

MLM schreef op zaterdag 02 juli 2011 @ 22:11:
Dat kan, maar je zit dan wel met kopietjes van F1 te werken (tis immers een pass by value)
Is gebruikelijk voor functors. Een non-const ref wil je ook niet want die bind weer niet aan temporaries, en dus krijg je de issue waar xos het over heeft. En een const ref heeft ook weer een const operator() nodig.

In C++-2011 gebruik je typisch een r-value reference: F1&& func.

[Voor 6% gewijzigd door .oisyn op 03-07-2011 22:55]

If I had a dollar for every time I didn't know what was going on, I'd be like: "Why am I always getting all this money?!"


Acties:
  • 0Henk 'm!

  • TheWickedD
  • Registratie: Juli 2002
  • Laatst online: 11-03 21:01
Bedankt xos and .oisyn,

Ik zie het probleem met de non-const reference, hoewel het hier wel compileerd, en ik zie dat std::transform van Microsoft hier idd ook gewoon per value gaat, dus de functor gaat weer per value. Verder vind ik het wrappen wel fijn omdat dit de form is die ik bijna altijd nodig heb.

Acties:
  • 0Henk 'm!

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 26-05 16:18
os.append(is.begin(), is.end()); is veel simpeler.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein

Pagina: 1


Tweakers maakt gebruik van cookies

Tweakers plaatst functionele en analytische cookies voor het functioneren van de website en het verbeteren van de website-ervaring. Deze cookies zijn noodzakelijk. Om op Tweakers relevantere advertenties te tonen en om ingesloten content van derden te tonen (bijvoorbeeld video's), vragen we je toestemming. Via ingesloten content kunnen derde partijen diensten leveren en verbeteren, bezoekersstatistieken bijhouden, gepersonaliseerde content tonen, gerichte advertenties tonen en gebruikersprofielen opbouwen. Hiervoor worden apparaatgegevens, IP-adres, geolocatie en surfgedrag vastgelegd.

Meer informatie vind je in ons cookiebeleid.

Sluiten

Toestemming beheren

Hieronder kun je per doeleinde of partij toestemming geven of intrekken. Meer informatie vind je in ons cookiebeleid.

Functioneel en analytisch

Deze cookies zijn noodzakelijk voor het functioneren van de website en het verbeteren van de website-ervaring. Klik op het informatie-icoon voor meer informatie. Meer details

janee

    Relevantere advertenties

    Dit beperkt het aantal keer dat dezelfde advertentie getoond wordt (frequency capping) en maakt het mogelijk om binnen Tweakers contextuele advertenties te tonen op basis van pagina's die je hebt bezocht. Meer details

    Tweakers genereert een willekeurige unieke code als identifier. Deze data wordt niet gedeeld met adverteerders of andere derde partijen en je kunt niet buiten Tweakers gevolgd worden. Indien je bent ingelogd, wordt deze identifier gekoppeld aan je account. Indien je niet bent ingelogd, wordt deze identifier gekoppeld aan je sessie die maximaal 4 maanden actief blijft. Je kunt deze toestemming te allen tijde intrekken.

    Ingesloten content van derden

    Deze cookies kunnen door derde partijen geplaatst worden via ingesloten content. Klik op het informatie-icoon voor meer informatie over de verwerkingsdoeleinden. Meer details

    janee