[C++] templates, pointers en casten *

Pagina: 1
Acties:

  • RickN
  • Registratie: December 2001
  • Laatst online: 14-06-2025
Hey,

ik ben een eigen pointer class aan het maken, die circulair gedrag vertoont tussen twee bounds.
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
template<class T>
class circ_pointer
{
private:
  T      *  m_ptr;  /* the actual pointer */
  int8_t *  m_base; /* the lowerbound of the circular memory region */
  size_t    m_size; /* size of the circular memory region in bytes */
public:
  /* constructors */
  circ_pointer(T * p = NULL)       : m_ptr(p), m_base(NULL),        m_size(0)                {}
  /* size in number of elements */
  circ_pointer(T * p, size_t size) : m_ptr(p), m_base((int8_t *)p), m_size(sizeof(T) * size) {}

  /* modifiers */
  circ_pointer<T> & operator += (ptrdiff_t);
  circ_pointer<T> & operator -= (ptrdiff_t);
  circ_pointer<T>   operator ++ (ptrdiff_t);
  circ_pointer<T>   operator -- (ptrdiff_t);

  circ_pointer<T>   operator +  (ptrdiff_t incr)  const { return circ_pointer<T>(*this) += incr; }
  circ_pointer<T>   operator -  (ptrdiff_t decr)  const { return circ_pointer<T>(*this) -= decr; }
  circ_pointer<T> & operator ++ ()                      { return *this += 1; }
  circ_pointer<T> & operator -- ()                      { return *this -= 1; }

  const T         & operator [] (ptrdiff_t index) const { return *(*this + index); }
        T         & operator [] (ptrdiff_t index)       { return *(*this + index); }
        T         & operator *  ()                const { return *m_ptr; }
}

/* hieronder nog wat implementaties... */

So far, so good, maar nu wil ik deze pointers ook, net als bij normale pointers, kunnen casten.
Specifiek, wil ik de volgende casts mogelijk maken:
code:
1
2
circ_ptr<T1>  <=>  circ_ptr<T2>
circ_ptr<T1>  <=>  T2 *

Cast syntax heb ik nog niet zo over na gedacht; liefst maak ik gewoon (TYPE)EXPR mogelijk, maar als er iets anders nodig is vind ik dat niet zo'n probleem.

Zover ben ik ook nog helemaal niet, want casten wil vooralsnog überhaupt niet lukken. Mijn "cast" pogingen lopen tot dusverre steevast uit op constructies van een nieuw object, waarbij wijzigingen niet worden gereflecteerd in de originele gecaste variable. Voorbeeld:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int array[10] = {0,1,2,3,4,5,6,7,8,9};
int * a = array;
/* a wijst naar 0 */
a++;
/* a wijst naar 1 */
((long *)a)++;
/* a wijst naar 2 (op mijn architectuur) */
((long long *)a)++;
/* a wijst naar 4 (op mijn architectuur) */

/* echter */
a = array;
/* a wijst naar 0 */
((circ_ptr<int>)a)++
/* a wijst naar 0, cast heeft dus een nieuw object gecreerd, en */
/* wijzigingen gaan verloren... */

Ik weet niet zo goed hoe ik hier nu mee verder moet. Ik heb al zitten denken aan een tweede class, identiek aan de eerste, maar met alle members als references. Deze class zou ik dan kunnen gebruiken bij het casten, maar dan moet ik weer een manier verzinnen om deze twee classes aan elkaar te praten, wat ook nog niet echt wil lukken.

Het gaat nu dus al mis als ik tussen T * en circ_ptr<T> wil casten, en het wordt alleen nog maar erger als ik het tussen T1 * en circ_ptr<T2> of circ_ptr<T1> en circ_ptr<T2> ga proberen.

Heeft iemand een goed idee om hiermee om te gaan?

[ Voor 10% gewijzigd door RickN op 01-07-2004 14:19 ]

He who knows only his own side of the case knows little of that.


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 12-05 22:23

curry684

left part of the evil twins

[C++] in de titel gefrot :)

Professionele website nodig?


Verwijderd

Wat je wilt is op deze manier per definitie onmogelijk, door een pointer (of wat voor type dan ook) te casten maak je een copie; als je een increment op die copie doet verandert de originele pointer natuurlijk niet mee. Assignment lost dat probleem op:

C++:
1
2
3
4
5
6
7
8
9
10
a= array;
/* a wijst naar 0 */
circ_pointer<int> b(a);
/* b is een copie van a */
b++;
/* a != b */
a= &*b;
/* a == b
 * gebruikt circ_pointer<int>::operator * ()
 */


Je zou dit kunnen vereenvoudigen door een casting-operator te definiëren:

C++:
1
2
3
4
5
6
7
template<class T>
class circ_pointer
{ 
  ...
  operator const T* () const { return m_ptr; }
  operator       T* ()       { return m_ptr; }
};


Dan wordt dat assignment uit het voorbeeld gewoon a= b; of zelfs a= ++b;

[ Voor 3% gewijzigd door Verwijderd op 01-07-2004 15:44 ]


  • RickN
  • Registratie: December 2001
  • Laatst online: 14-06-2025
Verwijderd schreef op 01 juli 2004 @ 15:43:
Wat je wilt is op deze manier per definitie onmogelijk, door een pointer (of wat voor type dan ook) te casten maak je een copie; als je een increment op die copie doet verandert de originele pointer natuurlijk niet mee.
Een cast is toch niets anders dan een herinterpretatie van reeds bestaande data? Er zou geen copy gemaakt moeten worden en een wijziging na casten moet effect hebben op de gecaste variable. De volgende code:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>

using namespace std;

int main()
{
  int a[10] = {0,257,2,3,4,5,6,7,8,9};
  int * p = a;
  
  cout << *p++ << ' ';
  cout << (int) *((char *)p)++ << ' ';
  cout << (int) *((char *)p)++ << ' ';
  cout << (int) *((char *)p)++ << ' ';
  cout << (int) *((char *)p)++ << ' ';
  cout << *p++ << ' ';
  cout << (int) *((long long *)p)++ << ' ';
  cout << *p++ << endl;
};

Geeft bij mij de volgende output:
code:
1
0 1 1 0 0 2 3 5

Met andere woorden, de increments op de pointer p na casten naar char * en long long * hebben weldegelijk effect op p zelf.

Het probleem waar ik nu tegenaan loop is dat bij casten naar mijn classe er wel een copy wordt gemaakt en increments dus geen weerslag hebben op het origineel.

Casting operatoren had ik al naar gekeken en die werken goed voor circ_ptr<T> => T * casts, maar en misschien ook wel voor circ_ptr<T1> => T2 * casts, maar (nog) niet zo goed voor de rest.

[ Voor 20% gewijzigd door RickN op 01-07-2004 17:32 ]

He who knows only his own side of the case knows little of that.


  • Eelis
  • Registratie: Januari 2003
  • Laatst online: 21-02-2015
.

[ Voor 115% gewijzigd door Eelis op 18-02-2015 19:25 ]


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
RickN schreef op 01 juli 2004 @ 17:27:
[...]
Een cast is toch niets anders dan een herinterpretatie van reeds bestaande data?
Nee, en ook nooit geweest. Hoe zou dat ook kunnen werken?
code:
1
2
int i = 2;
double d = (double) i;

Op veel implementaties is double groter dan int, 8 om 4 bytes. Toch is de code volstrekt helder, en het resultaat altijd 2.0. Volgens jouw beschrijving worden er 8-4 random bytes gelezen 8)7

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


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
RickN schreef op 01 juli 2004 @ 14:15:
Hey,

ik ben een eigen pointer class aan het maken, die circulair gedrag vertoont tussen twee bounds.
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template<class T>
class circ_pointer
{
private:
  T      *  m_ptr;  /* the actual pointer */
   /* constructors */
  circ_pointer(T * p = NULL)       : m_ptr(p), m_base(NULL),        m_size(0)                    
  /* modifiers */
  circ_pointer<T> & operator += (ptrdiff_t);
  circ_pointer<T> & operator -= (ptrdiff_t);
  circ_pointer<T>   operator ++ (int);
  circ_pointer<T>   operator -- (int);

}

/* hieronder nog wat implementaties... */

So far, so good, maar nu wil ik deze pointers ook, net als bij normale pointers, kunnen casten.
Specifiek, wil ik de volgende casts mogelijk maken:
code:
1
2
circ_ptr<T1>  <=>  circ_ptr<T2>
circ_ptr<T1>  <=>  T2 *

Cast syntax heb ik nog niet zo over na gedacht; liefst maak ik gewoon (TYPE)EXPR mogelijk, maar als er iets anders nodig is vind ik dat niet zo'n probleem.

Zover ben ik ook nog helemaal niet, want casten wil vooralsnog überhaupt niet lukken. Mijn "cast" pogingen lopen tot dusverre steevast uit op constructies van een nieuw object, waarbij wijzigingen niet worden gereflecteerd in de originele gecaste variable. Voorbeeld:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int array[10] = {0,1,2,3,4,5,6,7,8,9};
int * a = array;
/* a wijst naar 0 */
a++;
/* a wijst naar 1 */
((long *)a)++;
/* a wijst naar 2 (op mijn architectuur) */
((long long *)a)++;
/* a wijst naar 4 (op mijn architectuur) */

/* echter */
a = array;
/* a wijst naar 0 */
((circ_ptr<int>)a)++
/* a wijst naar 0, cast heeft dus een nieuw object gecreerd, en */
/* wijzigingen gaan verloren... */
Het kan, maar niet vragen hoe. Je originele gedrag is namelijk een niet-standaard extensie.

Casten van built-ins naar UDTs gaat met behulp van een 1-arg non-explicit ctor.
Omdat je de orginele a wil wijzigen moet circ_ptr<int> dus een reference naar de originele int* hebben. De circ_ptr is nu eenmaal een nieuw object. Vervolgens moet je circ_ptr<int>::operator++ de originele a wijzigen.

Het resultaat "werkt" voor je voorbeeld, maar dit dus niet:

C++:
1
2
3
4
5
circ_ptr<int> foo()
{
  static int bar[] = { 3,4 };
  return circ_ptr<int>( bar );
}

omdat er simpelweg geen int* bestaat. De volgende poging,
C++:
1
2
3
4
5
6
circ_ptr<int> foo()
{
  static int bar[] = { 3,4 };
  int * dummy = bar;
  return circ_ptr<int>( dummy );
}

werkt ook niet, omdat je dan buiten foo() een reference zou hebben naar dummy.
Kortom, bij het minste of geringste loop je tegen problemen aan. Doe dus zoals een ieder ander, en accepteer dat een cast altijd een nieuw temporary object oplevert.

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


  • RickN
  • Registratie: December 2001
  • Laatst online: 14-06-2025
Eelis schreef op 01 juli 2004 @ 20:01:
RickN:

Gcc 3.4.0 accepteert die code niet:

[...]

Ik vermoed dat gcc hier gelijk heeft..

Edit: Comeau geeft soortgelijke errors.
Ik werk hier op gcc 3.2.3 en die geeft echt de output zoals ik em aangaf. Ik geloof wel dat gcc 3.4 een volledig nieuwe, handgeschreven parser heeft gekregen dus misschien maakt dat het verschil.
MSalters schreef op 01 juli 2004 @ 21:32:
[...]

Nee, en ook nooit geweest. Hoe zou dat ook kunnen werken?
code:
1
2
int i = 2;
double d = (double) i;

Op veel implementaties is double groter dan int, 8 om 4 bytes. Toch is de code volstrekt helder, en het resultaat altijd 2.0. Volgens jouw beschrijving worden er 8-4 random bytes gelezen 8)7
Mja, lol, dat is idd iets dat ik me ook realiseerde toen ik al op de weg naar huis zat :D . Maar goed, een compiler liegt natuurlijk nooit :'( dus ik ging er toch nog maar even vanuit dat het bij gelijkwaardige types, zoals pointers, zo werkte, ik zag het tenslotte gebeuren.
Maar, begrijp me niet verkeerd, voor mij hoeft het niet zo te werken, het maakt me niet uit hoe het werkt. Ik wil alleen dat mijn pointers hetzelfde gedrag vertonen als conventionele pointers. Dat een cast een copy maakt maakt het voor mij alleen eenvoudiger, dus ik vind het allang goed...

He who knows only his own side of the case knows little of that.

Pagina: 1