Intefejs, szablony, pojemniki i ich iteratory

0

Witajcie.
Mam Wam ja takie cuś:
http://pastebin.com/yAvrMxbx

Co to robi? Ano, jest to trochę obudowany iterator. Dla pojemników STL'a oraz dla tablic i ich funkcji. Mam to na zadanie, generalnie :)
Oto testy:

/* 
 * File:   main.cpp
 * Author: kist
 *
 * Created on 12 maj 2011, 12:59
 */

#include <cstdlib>
#include <vector>
#include <list>
#include <set>
#include "ZrodloDanych.h"

using namespace std;

void wypisz(ZrodloDanych<int> &zd) {
	while (zd.nastepnyElement()) {
		cout << zd.aktualnaWartosc() << " ";
	}
}

struct ReferencjaDoInt {
	int& ref;
	ReferencjaDoInt(int & _ref): ref(_ref) {}
};

void test1() {
	cout << "std::vector\n";
	std::vector<int> liczby;
	for (int i = 0; i < 100; ++i)
		liczby.push_back(i);
	ZrodloDanych<int> zrodlo(liczby);
	wypisz(zrodlo);
}

void test2() {
	cout << "\n\nTablica dynamiczna\n";
	int* tablica = new int[100];
	for (int i = 0; i < 100; ++i)
		tablica[i] = i;
	ZrodloDanych<int> zrodlo(tablica, tablica + 100);
	wypisz(zrodlo);
}

void test3() {
	cout << "\n\nstd::set\n";
	std::set<int> liczby;
	for (int i = 0; i < 100; ++i)
		liczby.insert(i);
	std::set<int>::iterator poczatek=liczby.begin(), koniec = liczby.end();
	++poczatek;
	--koniec;
	ZrodloDanych<int> zrodlo(poczatek, koniec);
	wypisz(zrodlo);
}

void test4() {
	cout << "\n\nTablica statyczna\n";
	int tablica[100];
	for (int i = 0; i < 100; i++)
		tablica[i] = i;
	ZrodloDanych<int> zrodlo(tablica);
	wypisz(zrodlo);
}

void test5() {
	cout << "\n\nstd::list\n";
	list<int> liczby;
	for (int i = 0; i < 100; i++)
		liczby.push_front(i);
	ZrodloDanych<int> zrodlo(liczby);
	wypisz(zrodlo);
}

void test6() {
	std::vector<int> liczby;
	for (int i = 0; i < 100; ++i)
		liczby.push_back(i);

	cout << "\n\nReferencjaDoInt\n";
	ZrodloDanych<ReferencjaDoInt> referencyjneZrodlo(liczby);

	while(referencyjneZrodlo.nastepnyElement()) {
		referencyjneZrodlo.aktualnaWartosc().ref += 1;
	}
	ZrodloDanych<int> zrodlo(liczby);
	wypisz(zrodlo);
}

void test7() {
	cout << "\n\nZrodloDanych\n";
	std::vector<double> liczby;
	for (int i = 0; i < 100; ++i) {
		liczby.push_back((double)(i / 10));
	}

	ZrodloDanych<double> zrodlo_a(liczby);
	while(zrodlo_a.nastepnyElement())
		cout << zrodlo_a.aktualnaWartosc() << " ";
	
	
	ZrodloDanych<double> zrodlo_b(zrodlo_a);
	while(zrodlo_b.nastepnyElement())
		cout << zrodlo_b.aktualnaWartosc() << " ";
	
	cout << endl;
}

int
main(int argc, char** argv) {

	test1();
	test2();
	test3();
	test4();
	test5();
	test6();
	test7();

	return 0;
}

Otóż, kompilacja wysypuje się na ostatnim teście. Wiem, że dla klas szablonowych nie ma czegoś takiego jak szablonowy konstruktor kopiujący, ale jak w tym wypadku wprowadzić odpowiedni iterator ręcznie? Na typedefach nie zachodzi polimorfizm, prawda? Inną możliwością jest skonstruowanie ZrodloDanych::iterator, ale bez polimorfizmu znowu się tutaj nie obejdzie. Doradźcie mi coś, proszę. Próbowałem zrobić specjalizację dla typu double, ale testerka programów wywala się na jednym z innych testów.
Tutaj macie treść zadania: http://programowanie.ii.uj.edu.pl/problems/7//Adapter.pdf

Byłbym zobowiązany za jakiekolwiek wskazówki/podpowiedzi. Pozdrawiam.

0

Wiem, że dla klas szablonowych nie ma czegoś takiego jak szablonowy konstruktor kopiujący

eee... co? vector<T> ma konstrutor kopiujacy, brzmiacy vector<T>::vector(vector<T>const&) i nie ma z tym problemu.. jesli zas chcesz konstruktor-nie-kopiujacy, tylko 'konwertujacy', postaci a'la: template<X> vector<T>::vector(X const&) no to przeciez sam juz go napisales pod postacia template<STL_Con> Zrodlo<T>::Zrodlo(Stl_container&) ?? kompletnie nie rozumiem tego zdania co tutaj napisales

Na typedefach nie zachodzi polimorfizm, prawda?

eee.. co?s wybacz, malo to inteligentnie brzmi z mojej strony, a brzmisz mądrze, ale ni w ząb nie rozumiem:( typedef jest cechą statyczną kodu, jest staly w czasie kompilacji i dobrze znany. zero dynamizmu, wiec jako polimorfizm? vector<double>::iterator jest iteratorem<double>, vector<asddfg>::iterator jest iteratorem<asddfg> i to wiadomo tak samo dobrze jak to ze vector<double> jest vectorem<doublem>. chodzi Ci o takie pojecie 'polimorfizmu' gdzie vector<int> i vector<double> bylyby oba vectorem<cokolwiek>? jesli tak, to nie, nie ma.

Inną możliwością jest skonstruowanie ZrodloDanych::iterator, ale bez polimorfizmu znowu się tutaj nie obejdzie

ale przeciez polimorfizm juz masz uzyty w klasie bazowej basic_iter, wiec w czymże problem?? skoro juz go uzywasz, to sobie uzywaj, na zdrowie! a jak go nie chcesz bo cos tam, to i tak ten kod juz wymaga solidnej przebudowy, wiec dziura w postaci test7 to szczegol skoro trzeba zaczac prawie od nowa:)

--- a teraz do rzeczy..

bezposredni problem "niekompilowania się testu numer 7" w Twoim kodzie jest taki, ze linia

ZrodloDanych<double> zrodlo_b(zrodlo_a);

odpala ctor

template < typename STL_Container > ZrodloDanych(STL_Container &_orig) {

poniewaz on "najlepiej pasuje". zreszta, nawet innego nie ma ktory moglby.

ten ctor nie moze sie skompilowac, poniewaz uzywa on STL_Container::iterator i STL_Container::value_type, ktore nie istnieja, poniewaz w tym przypadku STL_Container==ZrodloDanych<double>, a przeciez Ty w ZrodloDanych NIE ZDEFINIOWALES nazw ani 'iterator' ani 'valuetype' !! co wiecej, nie ma tam tez metod begin ani end, ktorych ten ctor sie spodziewa.

albo wiec zdefiniuj dobry ctor ktory zostanie lepiej dopasowany (1), albo dostarcz te metody (2).

na opcje (2):
juz dopisanie w ZrodloDanych linijki

template < typename T >
class ZrodloDanych {
private:
	typedef __M_basic_it< T >* __T_iterator;

	typedef T value_type;          /// <----- tej tutaj

spowoduje ze liczba bledow kompilacji spada z 7 do 6. ot, skoro zakladasz ze kontenery maja takie skladowe, to Twoj kontener tez musi je miec! czeka Cie wiec napisanie typedefa dla 'iterator' oraz dostarczenie begin i end.. no, chyba ze nie chcesz 'byc kontenerem stl', w takim razie przechodzimy do...

...do opcji (1), czyli zadbanie o to, zeby copy-ctor nie probowal sie wiazac z ctor(STL_Container&)

primo: jako, ze operujesz na "basic iter", nie wiesz jaka jest jego implementacja! to znaczy, ze w zasadzie, ctor Twojego ZrodloDanych nie ma zielonego pojecia jaka jest faktyczna implementacja iteratora, wie tylko ze to-oryginalne-zrodlo ma w sobie __M_basic_it< T >*. jednak aby w ogole mowic o tworzeniu kopii zrodla danych, musisz jakos skonstruowac temu nowemu zrodlu calkiem nowy iter ktory bedzie odpowiadal iter'owi oryginalnego...

albo wiec redukujesz ten problem do (2) i do "pelnego udawania STL'a" i niech ZrodloDanych ma metody begin, end, i nazwy iterator i value_type, i na podstawie ich tworzysz calkiem nowy iter o znanym typie, zupelnie jak w ctorze stlcontainer - i w zasadzie nic nie robisz i uzywasz tego ctora.. albo tez (skoro juz wybrales opcje (1) czyli nie robimy tak jak napisalem przed tym nawiasem:))).. albo tez wiec trzeba jakos zapewnic mozliwosc "skopiowania" basic itera.

efektywny kod jest prostszy niz opisanie go:

	ZrodloDanych(ZrodloDanych<T> &_orig) {
		__M_iterator = _orig.__M_iterator->make_new_copy();   // <---- !
	}

fakt, nie wiesz czym jest faktycznie owo __M_basic_it< T >*, ale przeciez ONO WIE. niech wiec wykona kopie siebie samego, dynamiczna, stad wirtualna metoda 'new_copy' zeby zaznaczyc owo 'new', a wirtualna... no coz, przeciez cale __M_basic_it< T > jest po to, aby ustalic wirtualny interfejs, wiec chyba nie problem dorzucic metode w dodatku dosc poreczna skoro cctor'a nijak tam nie dolozysz..

no i oczywiscie więc również trzeba bedzie popelnic:

template < typename _TbasicType >
class __M_basic_it {

public:
	virtual _TbasicType operator *() = 0;
	virtual bool operator++()  = 0;

	virtual __M_basic_it<_TbasicType>* make_new_copy() = 0;   // <----- dodane
};

a i oraz faktyczna implementacja pozniej:

template < typename _Titerator, typename _Ttype, typename _TbasicType = _Ttype >
class __M_it : public __M_basic_it< _TbasicType > {
.....
....
public:
	__M_basic_it<_TbasicType>* make_new_copy() {     ///<----- dodane
		return new __M_it<_Titerator,_Ttype,_TbasicType>(__M_iterator, __M_end);   //<---- uwaga na pewien maly problem z BEGIN.. opis nizej
	}
}

po takich poprawkach (tzn. zaprezentowalem opcje (1) nie (2)!!), kod testu 7 sie skompiluje.
a czy bedzie dzialal pooprawnie? oj, niekoniecznie.
linijki:
(kod7 oryginalny)

        ZrodloDanych<double> zrodlo_a(liczby);
        while(zrodlo_a.nastepnyElement())             // <--- zuzycie iteratora w 100%
                cout << zrodlo_a.aktualnaWartosc() << " ";  
 
 
        ZrodloDanych<double> zrodlo_b(zrodlo_a);   // <--- kopia pustki!!!
        while(zrodlo_b.nastepnyElement())
                cout << zrodlo_b.aktualnaWartosc() << " ";

porownaj to z:
(kod7 inny)

        ZrodloDanych<double> zrodlo_a(liczby);
        ZrodloDanych<double> zrodlo_b(zrodlo_a);     // <--- kopia przed zuzyciem
        while(zrodlo_a.nastepnyElement())
                cout << zrodlo_a.aktualnaWartosc() << " ";
 
 
        while(zrodlo_b.nastepnyElement())
                cout << zrodlo_b.aktualnaWartosc() << " ";

kod7oryginalny sugeruje, ze po 'kopii' zrodla danych, chcialbys aby ta kopia powtarzala oryginalna sekwencje. ale Twoje zrodlo pamieta jedynie dwa iteratory: aktualny oraz koncowy. to znaczy, ze jezeli ze zrodla odczytasz N elementow, i potem skopiujesz zrodlo, to tak napisany cctor skpoiuje 'aktualny' iterator a nie startowy! kopia zrodla bedzie powtarzac sekwencje poczawszy od aktualnego stanu zastanego w momencie kopiowania i dlatego kod7oryginalny wypisze jeden zestaw, a kod7inny wypisze dwa zestawy. co wiecej, z racji tego ze zrodlodanych pamieta iteratory aktualny-oraz-koniec, nie masz szans sprawic aby to zaczelo zachowywac sie inaczej. musialbys do zrodladanych (albo do ktoregos m_iteratora) dostrzelic jeszcze zapamietywanie punktu startu.. no, chyba ze test7 faktycznie mial wypisywac wszystko-a-potem-nic i zle odebralem intecje.

edit: pufff.. troche tego wyszlo. ale mam nadzieje ze zrozumiale. jesli cos przeoczylem np. jakies zalozenia albo ograniczenia w postaci rozwiazania to przepraszam, pozno jest:/ opcje (2) przetestowalem u siebie na VS2008 i przynajmniej test7 dziala poprawnie. opcji (1) szczerze, nie chcialo mi sie troche, poniewaz przy wirutalnej bazie iteratora i nieujawnianiu jego konkretnego typu implementacji, napisanie begin/end jest troche wywrotowe -- one musza zwracac cos kopiowalnego, czyli trzebaby bylo dolozyc kolejna klaske-opakowanie, nie wirtualna, majaca copyctor, zawierajaca basic-iter i blablabla... i szybciej chyba byloby zrezygnowac z basiciter i sprobowac zrobic je typowane statycznie bez wirtualizmu.. dobra, zmeczylem sie, nie jestem w stanie ocenic sensownosci trzech ostatnich zdan ktore napisalem przed chwila. czas spac. ciao.

0

quetzalcoatl, dziękuję Ci serdecznie za odpowiedź :)

quetzalcoatl napisał(a)

eee... co? vector<T> ma konstrutor kopiujacy, brzmiacy vector<T>::vector(vector<T>const&) i nie ma z tym problemu.. jesli zas chcesz konstruktor-nie-kopiujacy, tylko 'konwertujacy', postaci a'la: template<X> vector<T>::vector(X const&) no to przeciez sam juz go napisales pod postacia template<STL_Con> Zrodlo<T>::Zrodlo(Stl_container&) ?? kompletnie nie rozumiem tego zdania co tutaj napisales

Chodzi mi o to, że konstruktor kopiujący oczywiście możemy sobie zdefiniować, ale nie będzie on nigdy wywołany dla klas szablonowych. Gdzieś nawet czytałem, czemu tak jest i zdaje się że to jest w oficjalnej referencji C++. Żeby copy ctor działał, trzeba zdefiniować swój konstruktor bez używania typu szablonowego. Whatever, nie zmienia to postaci rzeczy, że u mnie copy ctor nie jest wywoływany ;)

quetzalcoatl napisał(a)

eee.. co?s wybacz, malo to inteligentnie brzmi z mojej strony, a brzmisz mądrze, ale ni w ząb nie rozumiem:( typedef jest cechą statyczną kodu, jest staly w czasie kompilacji i dobrze znany. zero dynamizmu, wiec jako polimorfizm? vector<double>::iterator jest iteratorem<double>, vector<asddfg>::iterator jest iteratorem<asddfg> i to wiadomo tak samo dobrze jak to ze vector<double> jest vectorem<doublem>. chodzi Ci o takie pojecie 'polimorfizmu' gdzie vector<int> i vector<double> bylyby oba vectorem<cokolwiek>? jesli tak, to nie, nie ma.

Dokładnie o to mi chodziło, po prostu miałem nadzieję, że może jest jakiś sprytny kruczek, coś jak dynamic_cast albo const_cast, który pozwalał by na redefiniowane typów.

quetzalcoatl napisał(a)

ale przeciez polimorfizm juz masz uzyty w klasie bazowej basic_iter, wiec w czymże problem?? skoro juz go uzywasz, to sobie uzywaj, na zdrowie! a jak go nie chcesz bo cos tam, to i tak ten kod juz wymaga solidnej przebudowy, wiec dziura w postaci test7 to szczegol skoro trzeba zaczac prawie od nowa:)

Bardzo chętnie, ale nie mogę zrobić ZrodloDanych::iterator bez znajomości typu iteratora delikwenta, a ten typ poznaję dopiero w konstruktorze ;)

quetzalcoatl napisał(a)

na opcje (2):
juz dopisanie w ZrodloDanych linijki

template < typename T >
class ZrodloDanych {
private:
	typedef __M_basic_it< T >* __T_iterator;

	typedef T value_type;          /// <----- tej tutaj

spowoduje ze liczba bledow kompilacji spada z 7 do 6. ot, skoro zakladasz ze kontenery maja takie skladowe, to Twoj kontener tez musi je miec! czeka Cie wiec napisanie typedefa dla 'iterator' oraz dostarczenie begin i end.. no, chyba ze nie chcesz 'byc kontenerem stl', w takim razie przechodzimy do...

Jak w takim razie w moim wypadku zdefiniować typ ::iterator? Nie ma na to szansy. Z begin() i end() już bym sobie jakoś dał radę.

quetzalcoatl napisał(a)

no i oczywiscie więc również trzeba bedzie popelnic:

template < typename _TbasicType >
class __M_basic_it {

public:
	virtual _TbasicType operator *() = 0;
	virtual bool operator++()  = 0;

	virtual __M_basic_it<_TbasicType>* make_new_copy() = 0;   // <----- dodane
};</cpp>

a i oraz faktyczna implementacja pozniej:
```cpp
template < typename _Titerator, typename _Ttype, typename _TbasicType = _Ttype >
class __M_it : public __M_basic_it< _TbasicType > {
.....
....
public:
	__M_basic_it<_TbasicType>* make_new_copy() {     ///<----- dodane
		return new __M_it<_Titerator,_Ttype,_TbasicType>(__M_iterator, __M_end);   //<---- uwaga na pewien maly problem z BEGIN.. opis nizej
	}
}

po takich poprawkach (tzn. zaprezentowalem opcje (1) nie (2)!!), kod testu 7 sie skompiluje.

Niestety, nie.

++    -c -g -Wall -MMD -MP -MF build/Debug/GNU-Linux-x86/main.o.d -o build/Debug/GNU-Linux-x86/main.o main.cpp
ZrodloDanych.h: In constructor ‘ZrodloDanych<T>::ZrodloDanych(STL_Container&) [with STL_Container = ZrodloDanych<double>, T = double]’:
main.cpp:98:   instantiated from here
In file included from main.cpp:12:
ZrodloDanych.h:109: error: ‘class ZrodloDanych<double>’ has no member named ‘begin’
ZrodloDanych.h:109: error: ‘class ZrodloDanych<double>’ has no member named ‘end’
ZrodloDanych.h:109: error: no type named ‘iterator’ in ‘class ZrodloDanych<double>’
gmake[2]: *** [build/Debug/GNU-Linux-x86/main.o] Błąd 1
gmake[2]: Opuszczenie katalogu `/home/garrappachc/Dokumenty/Programiki/C++/Adapter'
gmake[1]: *** [.build-conf] Błąd 2
gmake[1]: Opuszczenie katalogu `/home/garrappachc/Dokumenty/Programiki/C++/Adapter'
gmake: *** [.build-impl] Błąd 2

Nie wiedzieć czemu, kompilator uparł się na tego konstruktora dla STL_Container. Mój kod wygląda obecnie tak:
http://pastebin.com/Y8juiA76

Kompiluję GCC w wersji 4.4.5, ale próbowałem także pod 4.5.2. W obu przypadkach wywala błąd.
Nie ma jakiegoś sposobu, aby zmusić kompilator do użycia tej a nie innej metody dla danego typu?

Pozdrawiam.

P.S. Rzeczywiście, w teście 7. był błąd :)

0

nie ma na to szansy

tak, nie ma. musialbys zrezygnowac z dynamizmu i w 100% przejsc na 'statyczny polimorfizm', wspomnialem o tym juz chyba

Niestety, nie.

no to mamy znowu roznice miedzy vs a gcc.. szkoda. niestety, w ktoryms z nich jest bug. gcc nie mam pod reka obecnie, a comeau jeszcze sobie nie kupilem:)

hm.. w tej chwili nie moge sobie wyobrazic, czemu przy template'ach dopasowywanie cctor'ow mialoby nie dzialac. niestety od paru lat nie pracuje juz w C/C++ na biezaco, wiec "intuicja" mi czasem zawodzi przy ciezszych tematach, ale przeciez resolver template'ow ma wbudowana zasade 'SFINAE' http://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error ktora wyraznie mowi, ze jesli jedna sciezka nie pasuje, ma sprobowac innych zamiast jej sie czepiac. Nawet w tym artykule na Wiki jest przyklad, gdzie za pomoca istnienia lub nie-istnienia T::type jest okreslane czy ma zostac wybrana funkcja A czy B, wiec aaaa.... czekaj. a czy to przypadkiem nie dziala tylko na warstwie nazw, a bledy we wlasciwych cialach juz nie sa brane pod uwage? chyba sobie musze pare rzeczy odswiezyc, eh

generalnie, caly czas wydaje mi sie, ze ten problem jest rozwiazywalny przy uzyciu narzedzi z boost::MPL, poniewaz one pozwalaja m.in. definiowac rozne typy zalezne na podstawie roznych warunkow narzuconych na typy parametrow template'a. wewnetrznie jest to sprytnie rozwiazywane za pomoca m.in. wyrazen ?: i w gruncie rzeczy, jest to dosc pokrecone:) sprobuje nad tym posiedziec chwile, ale, ja mam obecnie VS, gcc w ciagu najblizszego tygodnia na pewno nie dam rady dorwac.

0

Dzięki wielkie za pomoc :) Postaram się to jakoś inaczej rozwiązać, może istnieją jeszcze inne możliwości... W każdym razie, dzięki za dobre chęci i jakbyś dorwał tego gcc, to daj znać ;)

P.S. GCC jest chyba kompilatorem DevC++, jak mi się zdaje? Więc do tego nie trzeba nawet linuksa ;)

0

wiesz, nawet jakby byl, to nie ma specjalnie problemu z postawieniem sobie cygwina czy wirtualki, ale pracuje na nie moim sprzecie obecnie, wiec staram sie go maksymaline 'niezasmiecac' :P

#ifndef ZRODLODANYCH_H
#define	ZRODLODANYCH_H

#include <iostream>
#include <iterator>
#include <vector>

template < typename _TbasicType >
class __M_basic_it
{
public:
	virtual ~__M_basic_it() { }

	virtual _TbasicType operator *() = 0;
	virtual bool operator++()  = 0;
	virtual bool finished()=0;

	// __M_basic_it jest iteratorem 'samowiedzacym' o koncu, zas
	// copiable jest adapterem na __M_basic_it<> do schematu pary begin/end
	struct copiable
	{
		__M_basic_it<_TbasicType >* thebrain;

		~copiable(){delete thebrain;}
		copiable(copiable const & rhs){thebrain = rhs.thebrain == 0 ? 0 : rhs.thebrain->copyself_as_new();}

		copiable(__M_basic_it<_TbasicType > * brain){thebrain = brain;}

		_TbasicType operator *(){return **thebrain;}
		bool operator++(){return ++*thebrain;}
		bool operator==(copiable const & rhs) const {return rhs.thebrain==thebrain || rhs.thebrain==0 && thebrain->finished();}
		bool operator!=(copiable const & rhs) const {return !(*this == rhs);}
	};

	//+rzeczy potrzebne dla implementacji 'copiable'
	virtual __M_basic_it<_TbasicType>* copyself_as_new() const = 0;
	virtual copiable make_current_copiableiter() const = 0;
	virtual copiable make_end_copiableiter() const = 0;
	//-rzeczy
};

template < typename _Titerator, typename _Ttype, typename _TbasicType = _Ttype >
class __M_it : public __M_basic_it< _TbasicType >
{
private:
	_Titerator			__M_iterator;
	_Titerator			__M_end;

	//_Ttype const*		__M_cache;		do czego był Ci ten cacheing?

public:
	typedef _Titerator iterator;

	__M_it(const _Titerator &_begin, const _Titerator &_end) :
		__M_iterator(_begin),
		__M_end(_end)
		{}//,__M_cache(NULL) {}

	_TbasicType operator *()
	{
		//return _TbasicType(*const_cast<_Ttype*>(__M_cache));  // przeciez iteratory zazwyczaj odczyt maja bardzo lekki! poza tym, na cacheu mozna sie przejechac jesli pozniej oczekujesz pracy na prawdziwym elemencie
		return *__M_iterator;
	}

	bool operator++()
	{
		if ((__M_iterator) == __M_end)
			return false; // false jest zwracany jezeli ++ dotarlo do konca, ale ono juz tam moglo byc!
		
		// tutaj gdzies wywalilem linijke ustalajaca cache jako __M_cache = &*++__M_iterator co bylo bledne poniewaz T moze byc np "const int"
		// nie mowiac juz o tym, ze ++iter moglo wyjsc na END!

		++__M_iterator;
		
		if ((__M_iterator) == __M_end) // skoro moglo wyjsc, to warto to sprawdzic
			return false; // <-- z tego powodu
		
		return true;
	}

	//~__M_it() { delete __M_cache; } // ??????? nie mowiąc już o tym!!! przeciez _cache NIE mialo kopii tylko sie &owalo się do oryginalu!!

	//+rzeczy potrzebne dla implementacji 'copiable'
	bool finished() { return __M_iterator == __M_end; }
	__M_basic_it< _TbasicType >* copyself_as_new() const { return new __M_it(__M_iterator, __M_end); }
	copiable make_current_copiableiter() const { return copiable(copyself_as_new()); }
	copiable make_end_copiableiter() const { return copiable(0); } // ACHTUNG! brain=0 oznacza iterator koncowy, powniewaz current/__M_basic_it samo wie gdzie jest koniec, i iterator koncowy tak na prawde jest tylko markerem dla copiable::operator==/!=
	//-rzeczy
};

template < typename T >
class ZrodloDanych
{
private:
	typedef T value_type;
	typedef typename __M_basic_it< T >::copiable iterator;

	typedef __M_basic_it< T >* __T_iterator;

	__T_iterator __M_iterator;

	iterator begin() {return __M_iterator->make_current_copiableiter();}
	iterator end() {return __M_iterator->make_end_copiableiter();}

public:
	~ZrodloDanych()	{ delete __M_iterator; } // w przeciwienstwie do 'cache'a, tutaj musi byc delete, gdyz kazdy ctor robi new

	template < size_t N >
	ZrodloDanych(T (&_array)[N])
	{
		__M_iterator = new __M_it<
				const T*,
				T
			>(&_array[0], &_array[N]);
	}

	template < typename STL_Container >
	ZrodloDanych(STL_Container &_orig)
	{
		__M_iterator = new __M_it<
				typename STL_Container::iterator,
				typename STL_Container::value_type,
				T
			>(_orig.begin(), _orig.end());
	}

	template < typename STL_Container >
	ZrodloDanych(STL_Container const & _orig)
	{
		__M_iterator = new __M_it<
				typename STL_Container::const_iterator,
				typename STL_Container::value_type,
				T
			>(_orig.begin(), _orig.end());
	}

	template < typename STL_Iterator >
	ZrodloDanych(STL_Iterator const &_begin, STL_Iterator const &_end)
	{ 
		__M_iterator = new __M_it<
				STL_Iterator,
				T
			>(_begin, _end);
	}

	ZrodloDanych(T const *_ptr1, T const *_ptr2)
	{
		__M_iterator = new __M_it<
				const T*,
				T
			>(_ptr1, _ptr2);
	}

	bool nastepnyElement() { return ++(*__M_iterator); }

	T aktualnaWartosc() { return *(*__M_iterator); }
};

#endif	/* ZRODLODANYCH_H */

oraz

#include <vector>
#include <list>
#include <set>

#include "ZrodloDanych.h"
 
using namespace std;
 
template<typename T> struct Ref { T& ref; Ref(T& _ref): ref(_ref) {} };

size_t const N = 100;

void wypisz(ZrodloDanych<int> &zd)
{
	while (zd.nastepnyElement())
		cout << zd.aktualnaWartosc() << " ";
}

void test1()
{
	cout << "std::vector\n";

	vector<int> liczby;
	for (int i = 0; i < N; ++i)
		liczby.push_back(i);

	ZrodloDanych<int> zrodlo(liczby);
	wypisz(zrodlo);
}
 
void test2()
{
	cout << "\n\nTablica dynamiczna\n";
	
	int* tablica = new int[N];
	for (int i = 0; i < N; ++i)
		tablica[i] = i;
	
	ZrodloDanych<int> zrodlo(tablica, tablica + N);
	wypisz(zrodlo);
}
 
void test3()
{
	cout << "\n\nstd::set\n";

	std::set<int> liczby;
	for (int i = 0; i < N; ++i)
		liczby.insert(i);

	std::set<int>::iterator poczatek=liczby.begin(), koniec = liczby.end();
	//++poczatek;
	//--koniec;
	
	ZrodloDanych<int> zrodlo(poczatek, koniec);
	wypisz(zrodlo);
}
 
void test4()
{
	cout << "\n\nTablica statyczna\n";
	
	int tablica[N];
	for (int i = 0; i < N; i++)
		tablica[i] = i;

	ZrodloDanych<int> zrodlo(tablica);
	wypisz(zrodlo);
}
 
void test5()
{
	cout << "\n\nstd::list\n";

	list<int> liczby;
	for (int i = 0; i < N; i++)
		liczby.push_back(i); // liczby.push_front(i);

	ZrodloDanych<int> zrodlo(liczby);
	wypisz(zrodlo);
}
 
void test6() {
	cout << "\n\nReferencjaDoInt\n";

	std::vector<int> liczby;
	for (int i = 0; i < N; ++i)
		liczby.push_back(i);

	ZrodloDanych<Ref<int>> referencyjneZrodlo(liczby);

	while(referencyjneZrodlo.nastepnyElement())
		referencyjneZrodlo.aktualnaWartosc().ref += 1;
	
	ZrodloDanych<int> zrodlo(liczby);
    wypisz(zrodlo);
}
 
void test7()
{
	cout << "\n\nZrodloDanych\n";

	std::vector<double> liczby;
	for (int i = 0; i < N; ++i)
		liczby.push_back(i); // liczby.push_back(i / 10.0);

	ZrodloDanych<double> zrodlo_a(liczby);
	ZrodloDanych<double> zrodlo_b(zrodlo_a);

	while(zrodlo_a.nastepnyElement())
		cout << zrodlo_a.aktualnaWartosc() << " ";
	cout << endl;


	while(zrodlo_b.nastepnyElement())
		cout << zrodlo_b.aktualnaWartosc() << " ";
	cout << endl;
}
 
int main(int argc, char* argv[])
{
	test1();
	test2();
	test3();
	test4();
	test5();
	test6();
	test7();

	return 0;
}

no, ale co prawda sprawdzalem rowniez na VS :)

0

P.S. GCC jest chyba kompilatorem DevC++, jak mi się zdaje? Więc do tego nie trzeba nawet linuksa

DevC++ to stare już IDE ze staaarą wersją kompilatora.

Najnowsza wersja GCC (dla Linuksa) to 4.6.0.
Istnieją dwa porty GCC dla Windows: pakiet MinGW, obecnie na wersji gcc 4.5.2 oraz Cygwin (gcc 4.5.0).
Jest jeszcze DJGPP dla DOS-a, aktualnie jest tam gcc 4.5.3.

0

quetzalcoatl, dzięki za poprawę kodu, zaraz sprawdzę, jak działa.
Otóż, cache jest po to, żeby zwracać odpowiednią aktualnąWartość. W treści zadania powiedziane jest, że metoda aktualnaWartosc() nie będzie wywoływana bez poprzedzenia jej nastepnyElement(). Więc, jeżeli mamy wektor 0-100, to pętla w funkcji wypisz() powinna w tej postaci wypisać przy pierwszym wywołaniu 0. A testów nie edytuj, bo są robione pod testerkę :)

0

Dobra, przepraszam za zwłokę, ale miałem trochę roboty i na ZrodloDanych nie starczyło mi już czasu :)
Więc, po lekkich przeróbkach kodu:

// Michał Garapich

#ifndef ZRODLODANYCH_H
#define	ZRODLODANYCH_H


#include <iostream>
#include <iterator>
#include <vector>
 
template < typename _TbasicType >
class __M_basic_it {
public:
        virtual ~__M_basic_it() { }
 
        virtual _TbasicType operator *() = 0;
        virtual bool operator++()  = 0;
        virtual bool finished() = 0;
 
        // __M_basic_it jest iteratorem 'samowiedzacym' o koncu, zas
        // copiable jest adapterem na __M_basic_it<> do schematu pary begin/end
	struct copiable {
                __M_basic_it< _TbasicType >* thebrain;
 
                ~copiable() { delete thebrain; }

                copiable(copiable const &_rhs) {
			thebrain = _rhs.thebrain == 0 ? 0 : _rhs.thebrain -> copyself_as_new();
		}
 
                copiable(__M_basic_it< _TbasicType > * brain) { thebrain = brain; }
 
                _TbasicType operator *() { return **thebrain; }

                bool operator ++() { return ++*thebrain; }
                bool operator ==( copiable const &_rhs ) const { return _rhs.thebrain == thebrain || (_rhs.thebrain == 0 && thebrain -> finished()); }
                bool operator !=( copiable const &_rhs) const { return !(*this == _rhs); }
        };
 
        //+rzeczy potrzebne dla implementacji 'copiable'
        virtual __M_basic_it< _TbasicType >* copyself_as_new() const = 0;
        virtual copiable make_current_copiableiter() const = 0;
        virtual copiable make_end_copiableiter() const = 0;
        //-rzeczy
};
 
template < typename _Titerator, typename _Ttype, typename _TbasicType = _Ttype >
class __M_it : public __M_basic_it< _TbasicType > {
private:
        _Titerator		__M_iterator;
        _Titerator		__M_end;
 
        const _Ttype*		__M_cache;
 
public:
        typedef _Titerator iterator;
 
        __M_it(const _Titerator &_begin, const _Titerator &_end) :
                __M_iterator(_begin),
                __M_end(_end),
                __M_cache(NULL) {}
 
        _TbasicType operator *() {
                return _TbasicType(*const_cast<_Ttype*>(__M_cache));
        }
 
        bool operator ++() {
		__M_cache = &*__M_iterator;
                ++__M_iterator;
		if (__M_iterator == __M_end)
		  return false;
		return true;
        }
 
        //+rzeczy potrzebne dla implementacji 'copiable'
        typedef typename __M_basic_it< _TbasicType >::copiable __copiable;
        
        bool finished() { return __M_iterator == __M_end; }
        
        __M_basic_it< _TbasicType >* copyself_as_new() const { return new __M_it(__M_iterator, __M_end); }
        
        __copiable make_current_copiableiter() const { return __copiable(copyself_as_new()); }
        
        __copiable make_end_copiableiter() const { return __copiable(0); }
};
 
template < typename T >
class ZrodloDanych {
private:
        typedef T value_type;
        typedef typename __M_basic_it< T >::copiable iterator;
 
        typedef __M_basic_it< T >* __T_iterator;
 
        __T_iterator __M_iterator;
 
        iterator begin() {return __M_iterator->make_current_copiableiter();}
        iterator end() {return __M_iterator->make_end_copiableiter();}
 
public: 
	template < size_t N >
        ZrodloDanych(T (&_array)[N])
        {
                __M_iterator = new __M_it<
                                const T*,
                                T
                        >(&_array[0], &_array[N]);
        }
 
        template < typename STL_Container >
        ZrodloDanych(STL_Container &_orig)
        {
                __M_iterator = new __M_it<
                                typename STL_Container::iterator,
                                typename STL_Container::value_type,
                                T
                        >(_orig.begin(), _orig.end());
        }
 
        template < typename STL_Container >
        ZrodloDanych(STL_Container const & _orig)
        {
                __M_iterator = new __M_it<
                                typename STL_Container::const_iterator,
                                typename STL_Container::value_type,
                                T
                        >(_orig.begin(), _orig.end());
        }
 
        template < typename STL_Iterator >
        ZrodloDanych(STL_Iterator const &_begin, STL_Iterator const &_end)
        { 
                __M_iterator = new __M_it<
                                STL_Iterator,
                                T
                        >(_begin, _end);
        }
 
        ZrodloDanych(T const *_ptr1, T const *_ptr2)
        {
                __M_iterator = new __M_it<
                                const T*,
                                T
                        >(_ptr1, _ptr2);
        }
 
        bool nastepnyElement() { return ++(*__M_iterator); }
 
        T aktualnaWartosc() { return *(*__M_iterator); }
        
        ~ZrodloDanych() { delete __M_iterator; }
};
 
#endif        /* ZRODLODANYCH_H */

nie kompiluje się:

[garrappachc][Adapter] $ g++ -Wall -o adapter -g main.cpp 
In file included from main.cpp:12:
ZrodloDanych.h: In member function ‘bool __M_it<_Titerator, _Ttype, _TbasicType>::operator++() [with _Titerator = __M_basic_it<double>::copiable, _Ttype = double, _TbasicType = double]’:
main.cpp:122:   instantiated from here
ZrodloDanych.h:69: error: lvalue required as unary ‘&’ operand
[garrappachc][Adapter] $ 

Coś jest nie tak z tym copiable, chociaż idea jest zmyślna.

0

"Coś jest nie tak", ponieważ uparłeś się na swój "cache" i na swój sposób jego realizacji..

w tym przypadku w tej linii, __M_iterator jest typu '__M_basic_it<double>::copiable' (co nawet log kompilatora podpowiada).
próba wykonania __M_iterator wraca więc wynik operator() tegoż, czyli _TbasicType. Teraz przyjrzyj się jeszcze raz logu kompilatora - okazuje się że _TbasicType = double.

i teraz pytanie do Ciebie: jakże więc chciałbyś wykonać pozostałą część linii: __M_cache = &__M_iterator; poczynając od wzięcia adresu-z-wyniku?
Żeby to Ci się udało, operator
z copiable musiałby zwracać conajmniej double&. Nie możesz tak zrobić ot tak sobie napisać że ten operator zwraca _TbasicType&, ponieważ to z kolei wytnie Ci możliwość używania jakichkolwiek referencji jako _TbasicType w __M_basic_it [niech: operator zwraca tbasictype&, i niech tbasictype ustawisz na double&. efekt: operator zwraca double&& co jest niedopuszczalne]

w ogólności, proba pobrania adresu do elementu jest kiepskim pomysłem, poniewaz przy tak szeroko pojetej wymaganej uzywalnosci tego kodu, nie masz absolutnie żadnej pewności, że to po czym iterujesz ma faktycznie jakies trwałe miejsce w pamieci, a nie jest tylko tymczasówką która zaraz wyparuje, która przypadkiem przelatywala przez Twoj iterator/kontener. Inne uzasadnienie: jeżeli chcesz mieć parkę forward()/current(), to faktycznie musisz zapamietywac jakos "aktualna" pozycje, ale nie poprzez cacheowanie!! Twój kod, jako kontener/iterator, w ścislym rozumieniu jego funkcji, nie ma prawa odpalić żadnego operatora* na żadnej "pozycji" dopoki Twoj "klient" o to nie poprosi. Banalny powod: hen daleko w głębi, TWOJE źródło może może być iteratorowe, i ono, zamiast dostarczac Ci elementy, może na każdym swoim *-nięciu np. je generowac w czasie 10sekund per element odczytany - bo na przykład będzie łączyło się z serwerem giełdy i zwracało Ci dane archiwalne. Wyobraz sobie teraz jak Twoj caching zadziała, gdy ktoś weżmie Twoj iterator na pierwszy element kolekcji i przy jego pomocy przewinie się na element dwusetny i dopiero ten element faktycznie odczyta.

rozumiem, że chcesz sobie wziąć na niego & żeby go nie kopiować, ale w ogóle sam fakt pomyslu przytrzymywania tego czegos co przez Twoj kod przelatuje, jest "flawed", sorry za angielski, nie chce mi się szukać słownika..

musisz "cache" rozwiązać inaczej.
najlepiej, w ogóle bez niego.

po co w ogóle zapamietujesz "wartość*" czy tez "wartosc&" ? po co w ogole chcesz operowac na konkretnym "wartosc"?? przeciez Ty, czy raczej Twoj kod, nie ma zielonego pojecia czym ten typ jest, i 99% kodu kontenera i iteratora jest napisane tak a nie inaczej, zeby wlasnie NIE DOWIADYWAC sie tego i niczego nie zakladac. Jedyne na czym Twoj kod ma prawo operowac, to "sekwencja zrodlowa". Zamiast więc kombinować z jakims _TbasicType* czy _TbasicType&, użyj tego co na czym masz operowac: zapamiętaj poprzednią pozycję w sekwencji źródłowej, np. w operatorze++ zamiast zapamietywac _TbasicType*cache - zapamiętuj poprzedni _Titerator __M_iterator (ten sprzed wewnetrznego ++) i jego na biezaco uzywaj, potencjalnie wielokrotnie, jako generatora "aktualnej wartosci"

1 użytkowników online, w tym zalogowanych: 0, gości: 1