[C++] operator TYPE() en operator TYPE &()

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • Mijzelf
  • Registratie: September 2004
  • Niet online
Ik heb een 3th party library die functies bevat als deze:
C++:
1
2
3
4
5
typedef void *OBJECT;

CreateObject( OBJECT &out );
CopyObject( OBJECT in, OBJECT &out );
ReleaseObject( OBJECT in );
Voor elke Create of Copy moet Release aangeroepen worden op het gealloceerde object. Er zijn meer typen dan alleen OBJECT, maar ze zijn in feite allemaal void pointers.

Om geheugenlekken te voorkomen heb ik een wrapper template geschreven:
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
template<typename TYPE> class Object
{
    TYPE m_Object;
public:
    Object(): m_Object( 0 )
    {}
    ~Object()
    {
        Release();
    }
    void Release()
    {
        if( m_Object ) ReleaseObject( m_Object );
        m_Object = 0;
    }
    operator TYPE()
    {
        return m_Type;
    }
    operator TYPE &()
    {
        Release();
        return m_Type;
    }
};
Alle plekken waar een 'OBJECT object' staat is dat vervangen door een 'Object<OBJECT> object' .
Altijd als een referentie nodig is (en het object dus word overschreven) wordt het ding eerst gereleased.

Probleem is, het werkt niet. In alle gevallen wordt 'operator TYPE&()' aangeroepen, ook als 'operator TYPE()' nodig is. Het toevoegen van een 'operator TYPE() const' werkt ook niet.
Het vervangen van 'operator TYPE&()' door 'operator TYPE*()' en dan functie aanroepen als 'CopyObject( obj1, *obj2 )' werkt wel, alleen ben je dan de cleane automaat weer kwijt.

Zit ik nou tegen een compilerbug aan te kijken? (VS2010), of is dit te verwachten gedrag?

Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 08:34
De precieze regels voor overload resolution zijn complex. Misschien krijg je het gewenste gedrag als je operator TYPE() const markeert (wat 'ie gewoon is) maar eigenlijk zou ik je aanraden om niet impliciet de pointer vrij te geven bij conversie naar reference.

Waarschijnlijk kun je beter proberen de semantics (en de interface) van std::unique_ptr af te kijken. Die heeft ook een expliciete methode (release()) om de achterliggende pointer vrij te geven.

Acties:
  • 0 Henk 'm!

  • Mijzelf
  • Registratie: September 2004
  • Niet online
Tja. Ik heb hier een bestaande hoeveelheid code en die lekt. Zonder er erg diep in te willen duiken had ik dus bedacht om met #define's eenvoudig alle OBJECT declaraties te vervangen, en de bestaande Release() functies te verwijderen. Dus andere semantics zijn niet erg gewenst.

Overigens had ik de 'operator TYPE() const' al geprobeerd, zoals in mijn openingspost staat. Werkt niet.

Ik kan de impliciete release weglaten en de bestaande Release aanroepen vervangen door een Object<OBJECT>::Release(). Maar als er bestaande objecten worden hergebruikt zonder eerst te Release()n, lekt de code alsnog.

Acties:
  • 0 Henk 'm!

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
De "const" markering is niet magisch. Die betekent precies wat je eigenlijk best weet: "deze functie is ook aan te roepen op const objecten." Dat was je probleem niet.

Ik probeer alleen te begrijpen wat je precieze probleem is. Je zegt "als 'operator TYPE()' nodig is". Impliciet, "als het voor mijn code nodig is". Maar wat is die code dan precies? C++ gaat niet verder dan 1 expressie vooruit kijken om te bepalen welke conversie er nodig is. En vermoedelijk is de context waarin jij zo'n OBJECT nodig hebt er één waarin je feitelijk een OBJECT& nodig hebt.

Mocht je nu in de gelukkige omstandigheid zijn dat je feitelijk een OBJECT const& nodig hebt, dan is de triviale oplossing om een 'operator TYPE const&() const' te definieren. Die geeft betere (want kortere) conversie-sequenties, omdat je dan niet ook nog eens TYPE& naar TYPE const& nodig hebt.

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


Acties:
  • 0 Henk 'm!

  • Mijzelf
  • Registratie: September 2004
  • Niet online
Mijn feitelijke probleem is die 3th party library, en een hoeveelheid geërfde implementatie code. En die implementatiecode lekt geheugen.

Aangezien de libraryfuncties een OBJECT referentie altijd overschrijven (lees: geheugen alloceren) en een normaal OBJECT alleen lezen, dacht ik dat verschil te kunnen gebruiken om de code lekvrij te krijgen, door met een #define boven in de code alle OBJECT declaraties te vervangen door Object<OBJECT> declaraties, en alle ReleaseObject()'s te vervangen door een Object<OBJECT>::Release().

Eventueel zou ik dan nog in de Object<OBJECT> destructor kunnen kijken of de payload al gereleased is, en daarmee het feitelijke lek opsporen. Als ik om werk verlegen zit.

Helaas maakt de compiler dus het onderscheid niet. Bij de aanroep van de functie
CopyObject( OBJECT a, OBJECT &b );
word beide keren de 'operator TYPE &()' aangeroepen. Blijkbaar heeft (bij mijn compiler) bij gelijke geschiktheid de 'operator TYPE&()' de voorkeur boven 'operator TYPE()'.
MSalters schreef op zaterdag 14 maart 2015 @ 02:17:
De "const" markering is niet magisch. Die betekent precies wat je eigenlijk best weet: "deze functie is ook aan te roepen op const objecten."
Dat ben ik niet met je eens. Bij mijn weten zal een compiler bij gelijke geschiktheid altijd de const functies gebruiken. En dat is mooi, want dan kun je makkelijk Copy On Write functionaliteit inbouwen.
In dit geval zou de 'operator TYPE() const' voldoen, en zou ik dus verwachten dat hij voorrang heeft op 'operator TYPE &()'.

Ik neem aan dat dat niet het geval is, omdat normaal gesproken 'operator TYPE()' duurder is, aangezien er een kopie van TYPE wordt gemaakt. In dit speciale geval is de referentie net zo groot als het object zelf, is er geen dure copy constructor, en geldt dat dus niet.

[ Voor 30% gewijzigd door Mijzelf op 14-03-2015 17:50 ]


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 14-10 00:53
Zit je op Windows? In dat gevel kun je ook naar de debugging mogelijkheden van de Win32 API kunnen kijken : MSDN: Finding Memory Leaks Using the CRT Library

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 15-10 02:34

.oisyn

Moderator Devschuur®

Demotivational Speaker

Mijzelf schreef op zaterdag 14 maart 2015 @ 17:40:
Dat ben ik niet met je eens. Bij mijn weten zal een compiler bij gelijke geschiktheid altijd de const functies gebruiken.
Dat klopt dus niet :). De const overload wordt alleen aangeroepen als het object const is. Zou ook irritant zijn als dat niet zo is - een aanroen van begin() van een container zal dan altijd een const_iterator teruggeven. Sterker nog, het hele doel van overloaden is dan weg - de non-const variant zal immers nooit worden aangeroepen.

[ Voor 11% gewijzigd door .oisyn op 14-03-2015 21:09 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.

Pagina: 1