Forum: PC-Programmierung C++17/Templates: einfach gleichnamige Getter/Setter zuordnen


von cppbert3 (Gast)


Lesenswert?

Ich probiere über ein Template einen immer gleich bleibenden Code für
Property-Durchgriffe zu reduzieren, das Template macht im echten Code 
viel mehr, was aber für die Frage nicht relevant ist

das Mutable-Interface bekomme ich so von außen und darf es nicht ändern
in den Set/Get-Routinen passiert im Original-Code viel mehr - ist aber 
für die Frage hier auch nicht relevant - nur kann ich den Aufbau der 
Schnittstelle eben nicht ändern

zu folgendem Beispiel habe ich 2 Fragen:

1. warum kompiliert Test::setABC nicht
2. gibt es eine Möglichkeit bei dem Test::SetGet Template
Aufruf die "redundante" Nennung der beiden Zugriffs-Methode auf eine zu 
reduzieren?

bis einschließlich C++17 ist alles erlaubt

gcc.godbolt.org: https://gcc.godbolt.org/z/65x6Ef
1
#include <string>
2
3
class Mutable // das Interface bekomme ich so und kann es nicht aendern
4
{
5
public:
6
    //get ist immer: ValueType NAME() const
7
    //set ist immer: NAME(ValueType value_)
8
    //Primitive werden als einfaches Value returned der Rest als const&
9
10
    int XYZ() const { return m_XYZ; }
11
    void XYZ( int value_ ){ m_XYZ = value_; }
12
13
    const std::string& ABC() const { return m_ABC; }
14
    void ABC( const std::string& value_ ){ m_ABC = value_; }
15
16
    //... viele weitere setter/getter
17
18
private:
19
    int m_XYZ{};
20
    std::string m_ABC;
21
    //...
22
};
23
24
class Test
25
{
26
public:
27
    template <typename ValueType>
28
    void SetGet( ValueType ( Mutable::*getter )() const, void ( Mutable::*setter )( ValueType ), ValueType value_ )
29
    {
30
        // ich machen irgendwas mit dem Getter/Setter
31
        if((m_mutable.*getter)() == value_){ return; }
32
        (m_mutable.*setter)(value_);
33
    }
34
35
    void setXYZ( int value_ )
36
    {
37
        SetGet( &Mutable::XYZ, &Mutable::XYZ, value_ );
38
    }
39
40
    void setABC( const std::string& value_ )
41
    {
42
        SetGet( &Mutable::ABC, &Mutable::ABC, value_ );
43
    }
44
45
private:
46
    Mutable m_mutable;
47
};
48
49
int main()
50
{
51
    Test t;
52
    t.setXYZ( 34 );
53
    t.setABC( "100" );
54
    return 0;
55
}

falls sich jemand Frag was das mit meinem SinCos-Projekt zu tun hat:
gar nichts - ist ein völlig anderes Projekt

von Oliver S. (oliverso)


Lesenswert?

clang sagt doch eigentlich immer, was ihn stört.
Im dem Fall:
candidate template ignored: deduced conflicting types for parameter 
'ValueType' ('const std::basic_string<char> &' vs. 
'std::basic_string<char>')

Oliver

von mh (Gast)


Lesenswert?

cppbert3 schrieb:
>         SetGet( &Mutable::ABC, &Mutable::ABC, value_ );

Kann der Compiler hier zwischen den overloads unterscheiden? Das ist das 
erste was mir eingefallen ist und ich hab noch nicht weiter drüber 
nachgedacht.

von cppbert3 (Gast)


Lesenswert?

Oliver S. schrieb:
> clang sagt doch eigentlich immer, was ihn stört.
> Im dem Fall:
> candidate template ignored: deduced conflicting types for parameter
> 'ValueType' ('const std::basic_string<char> &' vs.
> 'std::basic_string<char>')

Hatte vergessen den Fehler zu posten, ich sehe leider nicht wo ich die 
const& Dekoration verliere?

von cppbert3 (Gast)


Lesenswert?

mh schrieb:
> cppbert3 schrieb:
>         SetGet( &Mutable::ABC, &Mutable::ABC, value_ );
>
> Kann der Compiler hier zwischen den overloads unterscheiden? Das ist das
> erste was mir eingefallen ist und ich hab noch nicht weiter drüber
> nachgedacht.

Ja über die Signatur die bei den beiden SetGet Parametern angegeben ist, 
hat aber nichts mit dem Fehler zu tun

von Oliver S. (oliverso)


Lesenswert?

Wenn man dem Compiler mit
1
SetGet<const std::string& >(

auf die Sprünge hilft, klappt es.

Oliver

von cppbert3 (Gast)


Lesenswert?

Oliver S. schrieb:
> Wenn man dem Compiler mitSetGet<const std::string& >(
>
> auf die Sprünge hilft, klappt es.
> Oliver

Ich würde lieber verstehen warum const& verloren geht anstatt dem 
Kompiler unter die Arme zu greifen

von cppbert3 (Gast)


Lesenswert?

Bis jetzt hab ich verstanden das beim dedukten der const qualifier immer 
verloren geht

Ich glaube der link führt zu einer möglichen Lösung: 
https://oopscenities.net/2014/02/01/c11-perfect-forwarding/

Beitrag #6504764 wurde von einem Moderator gelöscht.
von Oliver S. (oliverso)


Lesenswert?

Oliver S. schrieb:
> candidate template ignored: deduced conflicting types for parameter
> 'ValueType' ('const std::basic_string<char> &' vs.
> 'std::basic_string<char>')

Da in
1
void SetGet( ValueType ( Mutable::*getter )() const, void ( Mutable::*setter )( ValueType ), ValueType value_ )

"ValueType value_" ein pass-by-value Parameter ist, geht da die Referenz 
(und damit auch das const) verloren.

Perfect forwarding löst das Problem, dann hakt es aber bei der 
int-Version.

Oliver

von cppbert3 (Gast)


Lesenswert?

Oliver S. schrieb:
> Perfect forwarding löst das Problem, dann hakt es aber bei der
> int-Version.

da hänge ich jetzt auch

von Oliver S. (oliverso)


Lesenswert?

Mit
1
..., typename std::remove_const<ValueType>::type value_ )

funktioniert es.

Oliver

von udok (Gast)


Lesenswert?

Ich habe ja nichts gegen C++, aber wenn ich das C++ Chinesisch hier 
lese,
dann freue ich mich wieder richtig auf C.

von cppbert3 (Gast)


Lesenswert?

udok schrieb:
> Ich habe ja nichts gegen C++, aber wenn ich das C++ Chinesisch
> hier
> lese,
> dann freue ich mich wieder richtig auf C.

soo dolles chinesisch ist das gar nicht ich will nur was nicht ganz so 
typisches machen und lerne immer mal wieder gerne was neues, von Hand 
und mit Macros kann ich sowas ja schon seit Uhrzeiten :)

von cppbert3 (Gast)


Lesenswert?

Oliver S. schrieb:
> Mit
> ..., typename std::remove_const<ValueType>::type value_ )
> funktioniert es.
>
> Oliver

aber dann ist mein value_ ungewollt non-const - auch blöd, die 
Folge-Untertemplates (die hier im Beispiel nicht drin sind) erwartet die 
"Unversehrtheit" des Typs

von cppbert3 (Gast)


Lesenswert?

natürlich ist klar das ich auch den Tip von Oliver so nutzen kann

>>Oliver S. schrieb:
>> Wenn man dem Compiler mitSetGet<const std::string& >(
>> auf die Sprünge hilft, klappt es.

aber das decltype+type ist dann schon ein wenig haesslich
1
class Mutable // das Interface bekomme ich so und kann es nicht aendern
2
{
3
public:
4
    int XYZ() const { return m_XYZ; }
5
    void XYZ( int value_ ){ m_XYZ = value_; }
6
7
    const std::string& ABC() const { return m_ABC; }
8
    void ABC( const std::string& value_ ){ m_ABC = value_; }
9
10
    //... viele weitere setter/getter
11
12
private:
13
    int m_XYZ{};
14
    std::string m_ABC;
15
    //...
16
};
17
18
template <typename Class, typename ValueType>
19
using Getter = ValueType ( Class::* )() const;
20
21
template <typename Class, typename ValueType>
22
using Setter = void ( Class::* )( ValueType );
23
24
class Test
25
{
26
public:
27
    template <typename ValueType>
28
    void SetGet( Getter<Mutable, ValueType> getter_, Setter<Mutable, ValueType> setter_, ValueType value_ ){}
29
30
    void setXYZ( int value_ )
31
    {
32
        SetGet<decltype( value_ )>( &Mutable::XYZ, &Mutable::XYZ, value_ );
33
    }
34
35
    void setABC( const std::string& value_ )
36
    {
37
        SetGet<decltype( value_ )>( &Mutable::ABC, &Mutable::ABC, value_ );
38
    }
39
40
private:
41
    Mutable m_muteable;
42
};
43
44
int main()
45
{
46
    Test t;
47
    t.setXYZ( 34 );
48
    t.setABC( "100" );
49
    return 0;
50
}

von cppbert3 (Gast)


Lesenswert?

wo bleibt Niklas wenn man ihn braucht - hier bricht schon 
Gruppen-Depression aus :)

von Oliver S. (oliverso)


Lesenswert?

cppbert3 schrieb:
> aber dann ist mein value_ ungewollt non-const - auch blöd, die
> Folge-Untertemplates (die hier im Beispiel nicht drin sind) erwartet die
> "Unversehrtheit" des Typs

cppbert3 schrieb:
> //Primitive werden als einfaches Value returned der Rest als const&

Da const& eine Referenz auf ein const ist, ändert std::remove_const gar 
nichts am Typ. Das Rätsel ist für ich nur noch, warum es damit 
funktioniert.

Oliver
P.S. du kennst cppinsights?

von cppbert3 (Gast)


Lesenswert?

Oliver S. schrieb:
> cppbert3 schrieb:
>> aber dann ist mein value_ ungewollt non-const - auch blöd, die
>> Folge-Untertemplates (die hier im Beispiel nicht drin sind) erwartet die
>> "Unversehrtheit" des Typs
>
> cppbert3 schrieb:
>> //Primitive werden als einfaches Value returned der Rest als const&
>
> Da const& eine Referenz auf ein const ist, ändert std::remove_const gar
> nichts am Typ. Das Rätsel ist für ich nur noch, warum es damit
> funktioniert.
>
> Oliver
> P.S. du kennst cppinsights?

ja - wunderbares Tool um die Magie von Lambdas zu erklären :)

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

cppbert3 schrieb:
> wo bleibt Niklas wenn man ihn braucht - hier bricht schon
> Gruppen-Depression aus :)

Hihi, mir ist auch keine besonders gute Lösung eingefallen. Die 
vermutlich allgemeinste Lösung wäre:
1
class Test
2
{
3
public:
4
    template <auto Getter, auto Setter, typename Val>
5
    void SetGet(Val&& value_ )
6
    {
7
        // ich machen irgendwas mit dem Getter/Setter
8
        if((m_mutable.*Getter)() == value_){ return; }
9
        (m_mutable.*Setter)(std::forward<Val> (value_));
10
    }
11
12
    void setXYZ( int value_ )
13
    {
14
        SetGet <static_cast<int (Mutable::*) () const> (&Mutable::XYZ),
15
        static_cast<void (Mutable::*) (int)> (&Mutable::XYZ)> (value_);
16
    }
17
18
    void setABC( const std::string& value_ )
19
    {
20
        SetGet <static_cast<const std::string& (Mutable::*) () const> (&Mutable::ABC),
21
        static_cast<void (Mutable::*) (const std::string&)> (&Mutable::ABC)> (value_ );
22
    }
23
24
private:
25
    Mutable m_mutable;
26
};

Hat den kleinen Vorteil dass die Member-Pointer nicht zur Laufzeit 
verarbeitet werden müssen (effizienter). Das explizite Casten der 
Pointer via static_cast ist aber ziemlich lästig, aber mehr oder weniger 
nötig weil die Setter ja so inkonsistente Signaturen haben.

SetGet ist dafür ziemlich allgemein und macht kaum Annahmen über die 
Signaturen von Setter/Getter; der spezifische Teil ist in setXYZ und 
setABC gekapselt.

von Oliver S. (oliverso)


Lesenswert?

Es braucht noch nicht mal std::remove_const, ein einfacher "Zwischentyp" 
reicht aus. Erklären kann ich zwar immer noch nicht, wieso das den 
Compiler dazu bringt, die richtige type deduction zu finden, aber es 
funktioniert.

https://gcc.godbolt.org/z/Exj4h8

Oliver

von cppbert3 (Gast)


Lesenswert?

Oliver S. schrieb:
> Es braucht noch nicht mal std::remove_const, ein einfacher
> "Zwischentyp"
> reicht aus. Erklären kann ich zwar immer noch nicht, wieso das den
> Compiler dazu bringt, die richtige type deduction zu finden, aber es
> funktioniert.
>
> https://gcc.godbolt.org/z/Exj4h8
>
> Oliver
1
  template <typename T>
2
  struct TT
3
  {
4
    using type = T;
5
  };

ja das ist wirklich seltsam, wie bist du auf den Test gekommen?

von Vincent H. (vinci)


Lesenswert?

Sofern alle getter/setter Entweder value oder const-ref basiert sind 
könnte man den Compiler auch einfach fragen:
1
  template <typename ValueType,
2
            typename RefOrValue = std::conditional_t<
3
                std::is_reference_v<ValueType>, const ValueType&, ValueType> >
4
  void SetGet(Getter<Mutable, RefOrValue> getter_,
5
              Setter<Mutable, RefOrValue> setter_, ValueType value_) {}

von cppbert3 (Gast)


Lesenswert?

vielleicht wir dadurch irgendwie forciert das er den Type erst aus dem 
Setter oder Getter deducted und nicht dieser &-Verlust durch den 
value-Parameter passiert?

von cppbert3 (Gast)


Lesenswert?

Vincent H. schrieb:
> Sofern alle getter/setter Entweder value oder const-ref basiert
> sind
> könnte man den Compiler auch einfach fragen:
>   template <typename ValueType,
>             typename RefOrValue = std::conditional_t<
>                 std::is_reference_v<ValueType>, const ValueType&,
> ValueType> >
>   void SetGet(Getter<Mutable, RefOrValue> getter_,
>               Setter<Mutable, RefOrValue> setter_, ValueType value_) {}

ja das könnte so klappen - die komischerweise funktionierende Kurz-Form 
von Oliver wäre mir dann aber schon lieber

kann das jemand erklären?

von Vincent H. (vinci)


Lesenswert?

Vincent H. schrieb:
> Sofern alle getter/setter Entweder value oder const-ref basiert sind
> könnte man den Compiler auch einfach fragen:
>
>
1
>   template <typename ValueType,
2
>             typename RefOrValue = std::conditional_t<
3
>                 std::is_reference_v<ValueType>, const ValueType&, 
4
> ValueType> >
5
>   void SetGet(Getter<Mutable, RefOrValue> getter_,
6
>               Setter<Mutable, RefOrValue> setter_, ValueType value_) {}
7
>

Err ups, kleine Korrektur, der value_ am Ende wäre dann natürlich auch 
RefOrValue...

von Oliver S. (oliverso)


Lesenswert?

cppbert3 schrieb:
> ja das ist wirklich seltsam, wie bist du auf den Test gekommen?

Weil std::remove_const da ja auch nichts anders macht.

Oliver

von cppbert3 (Gast)


Lesenswert?

Oliver S. schrieb:
> cppbert3 schrieb:
>> ja das ist wirklich seltsam, wie bist du auf den Test gekommen?
>
> Weil std::remove_const da ja auch nichts anders macht.
>
> Oliver
1
template <typename T>
2
using TT = T;

geht nicht - und da sehen ich wirklich keinen Unterschied mehr

von cppbert3 (Gast)


Lesenswert?

cppbert3 schrieb:
> Oliver S. schrieb:
>> cppbert3 schrieb:
>>> ja das ist wirklich seltsam, wie bist du auf den Test gekommen?
>>
>> Weil std::remove_const da ja auch nichts anders macht.
>>
>> Oliver
> template <typename T>
> using TT = T;
>
> geht nicht - und da sehen ich wirklich keinen Unterschied mehr

d.h. durch den struct TT Scope wird da irgendwas anders deducted

von cppbert3 (Gast)


Lesenswert?

btw passiert mir sowas ständig - ich denke etwas müsste so funktionieren 
und dann stolpere ich durch die Untiefen der 
Templare-Parameter-Deduction und dann schließen noch andere mit an :)

von Oliver S. (oliverso)


Lesenswert?

cppbert3 schrieb:
> vielleicht wir dadurch irgendwie forciert das er den Type erst aus dem
> Setter oder Getter deducted und nicht dieser &-Verlust durch den
> value-Parameter passiert?

Den Typ deducted der ja auch in der Originalversion aus allen drei 
Parametern, nur kommt er da ohne den Zwischentyp auf const std::string& 
für die Funktionspointer, während er für value_ den einfachsten Weg geht 
und einen std::string deducted. Das führt dann zur o.a. Fehlermeldung.

Oliver

: Bearbeitet durch User
von cppbert3 (Gast)


Lesenswert?

Niklas G. schrieb:
> Hat den kleinen Vorteil dass die Member-Pointer nicht zur Laufzeit
> verarbeitet werden müssen (effizienter). Das explizite Casten der
> Pointer via static_cast ist aber ziemlich lästig, aber mehr oder weniger
> nötig weil die Setter ja so inkonsistente Signaturen haben.

d.h. du gehst davon aus das Olivers Zwischen-Struct Lösung mehr 
calls/code erzeugt? (ist bei gcc.godbolt ein wenig schwer zu erkennen)
1
    template <typename ValueType>
2
    void SetGet( ValueType ( Mutable::*getter )() const,
3
        void ( Mutable::*setter )( ValueType ),
4
      typename TT<ValueType>::type value_
5
      )
6
    {
7
        // ich machen irgendwas mit dem Getter/Setter
8
        if((m_mutable.*getter)() == value_){ return; }
9
        (m_mutable.*setter)(value_);
10
    }
11
12
    void setXYZ( int value_ )
13
    {
14
        SetGet( &Mutable::XYZ, &Mutable::XYZ, value_ );
15
    }
16
17
    void setABC( const std::string& value_ )
18
    {
19
        SetGet( &Mutable::ABC, &Mutable::ABC, value_ );
20
    }

von Oliver S. (oliverso)


Lesenswert?

Ich hab jetzt die Erklärung, warum das mit dem Zwischentyp funktioniert.
Damit wird er Typ von value_ nicht detuctible.

https://stackoverflow.com/a/12566270

Oliver

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

cppbert3 schrieb:
> d.h. du gehst davon aus das Olivers Zwischen-Struct Lösung mehr
> calls/code erzeugt?

Ja, wenn SetGet nicht inlined wird. "getter" und "setter" sind ja 
Funktions-Parameter, welche also auch übergeben werden müssen. Das kann 
man mit "__attribute__ ((noinline))" ausprobieren.

von Oliver S. (oliverso)


Lesenswert?

Der Zwischentyp erzeugt keine Code, der sorgt nur dafür, daß der 
Compiler den Typ von value_ richtig (=compilierbar) erkennt.

Oliver

von cppbert3 (Gast)


Lesenswert?

Oliver S. schrieb:
> Der Zwischentyp erzeugt keine Code, der sorgt nur dafür, daß der
> Compiler den Typ von value_ richtig (=compilierbar) erkennt.
>
> Oliver

das ist mir klar - ich denke ohne Struct oder mit using ist es nur ein 
alias - in dem Struct ist es ein Struct-lokaler Type der anders gewertet 
wird

von cppbert3 (Gast)


Lesenswert?

Niklas G. schrieb:
> SetGet <static_cast<const std::string& (Mutable::*) () const>
> (&Mutable::ABC),
>         static_cast<void (Mutable::*) (const std::string&)>
> (&Mutable::ABC)> (value_ );

du hast recht dein Code wird erzeugt den selben Code als hätte man es 
von Hand ausgeschrieben, Olivers Stand (der schöner aussieht) macht 
einen call für setXYZ(int)

also konzentriere ich mich mal auf deinen Stand - weil ich möchte 
definitiv nicht ein Template einführen und damit dann auch noch die 
inline-Fähigkeit stören

aber trotzdem Danke Oliver! für deine Lösungen

von cppbert3 (Gast)


Lesenswert?

cppbert3 schrieb:
> Niklas G. schrieb:
>> SetGet <static_cast<const std::string& (Mutable::*) () const>
>> (&Mutable::ABC),
>>         static_cast<void (Mutable::*) (const std::string&)>
>> (&Mutable::ABC)> (value_ );
>
> du hast recht dein Code wird erzeugt den selben Code als hätte man es
> von Hand ausgeschrieben, Olivers Stand (der schöner aussieht) macht
> einen call für setXYZ(int)
>
> also konzentriere ich mich mal auf deinen Stand - weil ich möchte
> definitiv nicht ein Template einführen und damit dann auch noch die
> inline-Fähigkeit stören
>
> aber trotzdem Danke Oliver! für deine Lösungen

und auch danke an die anderen

jetzt muss ich nur noch das ge-caste ein wenig kleiner bekommen - ich 
bau mal ein trait das mir fuer einen type die Setter/Getter-types 
liefert und schaue ob dabei mein inlining wieder zerstört wird

von Oliver S. (oliverso)


Lesenswert?

cppbert3 schrieb:
> du hast recht dein Code wird erzeugt den selben Code als hätte man es
> von Hand ausgeschrieben, Olivers Stand (der schöner aussieht) macht
> einen call für setXYZ(int)

So, wie er Code da steht (also mit inlining) erzeugen beide Varianten 
den gleichen Code. Bei solch kurzen Programmen behält der Optimierer da 
schon den Überblick.

Trotzdem würde ich auch die Version von Niklas bevorzugen. 
Funktionspointer sind ja sowas von C, wenn es aber nunmal nicht ohne 
geht, dann wenigstens so C++"isch", wie möglich ;)

Oliver

: Bearbeitet durch User
von cppbert3 (Gast)


Lesenswert?

Oliver S. schrieb:
> cppbert3 schrieb:
>> du hast recht dein Code wird erzeugt den selben Code als hätte man es
>> von Hand ausgeschrieben, Olivers Stand (der schöner aussieht) macht
>> einen call für setXYZ(int)
>
> So, wie er Code da steht (also mit inlining) erzeugen beide Varianten
> den gleichen Code. Bei solch kurzen Programmen behält der Optimierer da
> schon den Überblick.
>
> Trotzdem würde ich auch die Version von Niklas bevorzugen.
> Funktionspointer sind ja sowas von C, wenn es aber nunmal nicht ohne
> geht, dann wenigstens so C++"isch", wie möglich ;)
>
> Oliver

nein ich habs getestet der clang und der gcc inlinen nicht - weil man 
Funktione-Pointer Parameter nicht optimieren darf, wenn die als Template 
value type Parameter komme aber schon

das hier ist jetzt meine entgültige Version des Prototyps
- volles inlineing und das hilfs-template kann von aussen kommen :)

gcc.godbolt.org: https://gcc.godbolt.org/

relativ kurz und schnuckelig
1
#include <string>
2
3
    class Mutable // das Interface bekomme ich so und kann es nicht aendern
4
    {
5
    public:
6
        //get ist immer: ValueType NAME() const
7
        //set ist immer: NAME(ValueType value_)
8
        //Primitive werden als einfaches Value returned der Rest als const&
9
10
        int XYZ() const
11
        {
12
            return m_XYZ;
13
        }
14
        void XYZ( int value_ )
15
        {
16
            m_XYZ = value_;
17
        }
18
19
        const std::string& ABC() const
20
        {
21
            return m_ABC;
22
        }
23
        void ABC( const std::string& value_ )
24
        {
25
            m_ABC = value_;
26
        }
27
28
        //... viele weitere setter/getter
29
30
    private:
31
        int m_XYZ{};
32
        std::string m_ABC;
33
        //...
34
    };
35
36
    template <typename Class, typename ValueType>
37
    using Getter = ValueType ( Class::* )() const;
38
39
    template <typename Class, typename ValueType>
40
    using Setter = void ( Class::* )( ValueType );
41
42
    template <typename Class,
43
              typename ValueType,
44
              Setter<Class, ValueType> SetterIntf,
45
              Getter<Class, ValueType> GetterIntf>
46
    inline void SetGet( Class& object_, ValueType value_ )
47
    {
48
        // ich machen irgendwas mit dem Getter/Setter
49
        if( ( object_.*GetterIntf )() == value_ )
50
        {
51
            return;
52
        }
53
        ( object_.*SetterIntf )( value_ );
54
    }
55
56
    class Test
57
    {
58
    public:
59
        void setXYZ( int value_ )
60
        {
61
            SetGet<Mutable, decltype( value_ ), &Mutable::XYZ, &Mutable::XYZ>( m_mutable, value_ );
62
        }
63
64
        void setABC( const std::string& value_ )
65
        {
66
            SetGet<Mutable, decltype( value_ ), &Mutable::ABC, &Mutable::ABC>( m_mutable, value_ );
67
        }
68
69
    private:
70
        Mutable m_mutable;
71
    };
72
73
    int test()
74
    {
75
        Test t;
76
77
        int i = 3;
78
        int& ri = i;
79
        const int ci = 3;
80
        const int& cri = ci;
81
82
        t.setXYZ( 34 );
83
        t.setXYZ( i );
84
        t.setXYZ( ri );
85
        t.setXYZ( ci );
86
        t.setXYZ( cri );
87
        //
88
        std::string s{ "hallo" };
89
        std::string& rs = s;
90
        const std::string cs{ "hallo" };
91
        const std::string& crs = cs;
92
93
        typedef std::remove_const_t<decltype( crs )> type1;
94
        static_assert( std::is_same<const std::string&, type1>::value, "nok" );
95
96
        typedef std::remove_const_t<decltype( cs )> type2;
97
        static_assert( std::is_same<std::string, type2>::value, "nok" );
98
99
        typedef std::remove_const_t<decltype( s )> type2;
100
        static_assert( std::is_same<std::string, type2>::value, "nok" );
101
102
        t.setABC( "100" );
103
        t.setABC( s );
104
        t.setABC( rs );
105
        t.setABC( cs );
106
        t.setABC( crs );
107
108
        return 0;
109
    }
110
111
    int main()
112
    {
113
        return test();
114
    }

von cppbert3 (Gast)


Lesenswert?

mein letzter Stand

gcc.godbolt.org: https://gcc.godbolt.org/z/db4rP6
1
#include <string>
2
3
    class Mutable // das Interface bekomme ich so und kann es nicht aendern
4
    {
5
    public:
6
        //get ist immer: ValueType NAME() const
7
        //set ist immer: NAME(ValueType value_)
8
        //Primitive werden als einfaches Value returned der Rest als const&
9
10
        int XYZ() const
11
        {
12
            return m_XYZ;
13
        }
14
        void XYZ( int value_ )
15
        {
16
            m_XYZ = value_;
17
        }
18
19
        const std::string& ABC() const
20
        {
21
            return m_ABC;
22
        }
23
        void ABC( const std::string& value_ )
24
        {
25
            m_ABC = value_;
26
        }
27
28
        //... viele weitere setter/getter
29
30
    private:
31
        int m_XYZ{};
32
        std::string m_ABC;
33
        //...
34
    };
35
36
    template <typename Class, typename ValueType>
37
    using Getter = ValueType ( Class::* )() const;
38
39
    template <typename Class, typename ValueType>
40
    using Setter = void ( Class::* )( ValueType );
41
42
    template <typename ValueType,
43
              typename Class,
44
              Setter<Class, ValueType> SetterIntf,
45
              Getter<Class, ValueType> GetterIntf>
46
    inline void OuterSetGet( Class& object_, ValueType value_ )
47
    {
48
        // ich machen irgendwas mit dem Getter/Setter
49
        if( ( object_.*GetterIntf )() == value_ )
50
        {
51
            return;
52
        }
53
        ( object_.*SetterIntf )( value_ );
54
    }
55
56
    class Test
57
    {
58
    private:
59
        template <typename ValueType, Setter<Mutable, ValueType> SetterIntf, Getter<Mutable, ValueType> GetterIntf>
60
        inline void InnerSetGet( ValueType value_ )
61
        {
62
            OuterSetGet<decltype( value_ ), Mutable, SetterIntf, GetterIntf>( m_mutable, value_ );
63
        }
64
65
    public:
66
        void setXYZ( int value_ )
67
        {
68
            InnerSetGet<decltype( value_ ), &Mutable::XYZ, &Mutable::XYZ>( value_ );
69
        }
70
71
        void setABC( const std::string& value_ )
72
        {
73
            InnerSetGet<decltype( value_ ), &Mutable::ABC, &Mutable::ABC>( value_ );
74
        }
75
76
    private:
77
        Mutable m_mutable;
78
    };
79
80
    int test()
81
    {
82
        Test t;
83
84
        int i = 3;
85
        int& ri = i;
86
        const int ci = 3;
87
        const int& cri = ci;
88
89
        t.setXYZ( 34 );
90
        t.setXYZ( i );
91
        t.setXYZ( ri );
92
        t.setXYZ( ci );
93
        t.setXYZ( cri );
94
        //
95
        std::string s{ "hallo" };
96
        std::string& rs = s;
97
        const std::string cs{ "hallo" };
98
        const std::string& crs = cs;
99
100
        t.setABC( "100" );
101
        t.setABC( s );
102
        t.setABC( rs );
103
        t.setABC( cs );
104
        t.setABC( crs );
105
106
        return 0;
107
    }
108
109
    int main()
110
    {
111
        return test();
112
    }

Danke an alle

von Oliver S. (oliverso)


Lesenswert?

cppbert3 schrieb:
> nein ich habs getestet der clang und der gcc inlinen nicht - weil man
> Funktione-Pointer Parameter nicht optimieren darf, wenn die als Template
> value type Parameter komme aber schon

Da in beiden Versionen sämtliche Parameter zur Compilezeit bekannt sind, 
optimiert der Compiler das in beiden Fällen soweit zusammen, wie es 
geht.

https://gcc.godbolt.org/z/61Ke6K

Oliver

: Bearbeitet durch User
von cppbert3 (Gast)


Lesenswert?

Oliver S. schrieb:
> cppbert3 schrieb:
>> nein ich habs getestet der clang und der gcc inlinen nicht - weil man
>> Funktione-Pointer Parameter nicht optimieren darf, wenn die als Template
>> value type Parameter komme aber schon
>
> Da in beiden Versionen sämtliche Parameter zur Compilezeit bekannt sind,
> optimiert der Compiler das in beiden Fällen soweit zusammen, wie es
> geht.
>
> https://gcc.godbolt.org/z/61Ke6K
>
> Oliver

hatte ich auch gedacht und jetzt auch durch dich bestätigt, es gab bei 
mir zeitweise calls auf setXYZ im Code (mit -O2) das ist mit deiner 
Variante passiert und mit Niklas Variante nicht - ich hätte auch 
erwartet das beides inlined

dein gcc.godbolt Link von vom 09.12.2020 11:01 inlined nicht wenn er mit 
dem gcc gebaut wird - clang macht inlining, wenn es kompiletime 
parameter sind machen es beide
https://gcc.godbolt.org/z/bMa6rc

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.