Funkcja przyjmująca kilka typów argumentów

0

Witam, tak jak w temacie..

Pokaże na przykładzie kodu.

mam dwie funkcje

bool collision(TImage * a, TShape * b)

i

bool collisionB(TShape * a, TShape * b)

Robią dokładnie to samo i pytanie czy da się to rozwiącać w taki sposób zebym w pierwszym parametrcie mógł przyjmować TShape i TImage?

0

Da się. Ale dam Ci na przykładzie TLabel i TEdit, bo nie mam czasu na inne testy. Dopasuj to sobie do Twoich komponentów i potrzeb.

template <typename T> bool __fastcall Test(T);

template <typename T> bool __fastcall Test(T x)
{
//......

dynamic_cast<TLabel*>(x)->Caption = "test";
dynamic_cast<TEdit*>(x)->Text = "test2";

//................
return true;
}


Test(Label1);
Test(Edit1);
1

Mniej więcej coś takiego:

bool xcollision(Object * a, TShape * b)
begin
    if (TypeOf(a) = TypeOf(TImage)) then
    begin
        xcollision = collision(a, b)
    end;
    if (TypeOf(a) = TypeOf(TShape)) then
    begin
        xcollision = collisionB(a, b)
    end;
end;

Powiedziałbym, że na pewno źle zapisałem, ale powinno to nakierować, jak to zrobić. Potrzebne są następujące rzeczy.

  1. Sprawdź, po jakim typie dziedziczą TImage i TShape i taki typ powinna przyjmować Twoja funkcja. Wszystkie typy dziedziczą po TObject lub Object (nie pamiętam który) i taki można podać jako parametr.
  2. Poszukaj funkcji, która określa, czy dany obiekt jest danego typu. Nie pamiętam, jak to się robi w Object Pascal, ale myślę, że wiadomo o co chodzi.
  3. W zależności od tego, jakiego faktycznego typu masz obiekt, wywołujesz raz jedną, raz drugą funkcję.
0
  1. Sprawdź, po jakim typie dziedziczą TImage i TShape i taki typ powinna przyjmować Twoja funkcja. Wszystkie typy dziedziczą po TObject lub Object (nie pamiętam który) i taki można podać jako parametr.

Na to nie wpadłem, będę kombinował ale myśle że już ogarnę. Dziękuje bardzo.

jeszcze jeden problem mam

nad graczem chce dodać ile zostało mu życia;

PlayerInfo->Caption = "HP: " + to_string(playerHP);

playerHP to zmienna typu float

ale wyrzuca błąd przy kompilowaniu

[bcc32c Error] Unit1.cpp(87): no viable conversion from 'basic_string<char, std::char_traits<char>, std::allocator<char> >' to 'Vcl::Controls::TCaption' (aka 'System::UnicodeString')
  ustring.h(94): candidate constructor not viable: no known conversion from 'basic_string<char, std::char_traits<char>, std::allocator<char> >' to 'const char *' for 1st argument
  ustring.h(95): candidate constructor not viable: no known conversion from 'basic_string<char, std::char_traits<char>, std::allocator<char> >' to 'const System::UnicodeString &' for 1st argument

jak to sformatować ?

1
Starmi napisał(a):
  1. Sprawdź, po jakim typie dziedziczą TImage i TShape i taki typ powinna przyjmować Twoja funkcja. Wszystkie typy dziedziczą po TObject lub Object (nie pamiętam który) i taki można podać jako parametr.

Na to nie wpadłem, będę kombinował ale myśle że już ogarnę. Dziękuje bardzo.

jeszcze jeden problem mam

nad graczem chce dodać ile zostało mu życia;

PlayerInfo->Caption = "HP: " + to_string(playerHP);

playerHP to zmienna typu float

ale wyrzuca błąd przy kompilowaniu

[bcc32c Error] Unit1.cpp(87): no viable conversion from 'basic_string<char, std::char_traits<char>, std::allocator<char> >' to 'Vcl::Controls::TCaption' (aka 'System::UnicodeString')
  ustring.h(94): candidate constructor not viable: no known conversion from 'basic_string<char, std::char_traits<char>, std::allocator<char> >' to 'const char *' for 1st argument
  ustring.h(95): candidate constructor not viable: no known conversion from 'basic_string<char, std::char_traits<char>, std::allocator<char> >' to 'const System::UnicodeString &' for 1st argument

jak to sformatować ?

PlayerInfo->Caption = "HP: " + (UnicodeString)playerHP;
0

Działa. Dziekuje.

a mógłbyś mi rozjaśnić w czym był problem?

1

@Starmi niestety nigdy nie używałem to_string więc się nie wypowiem :)

5

Właściwe pytanie brzmi jak wygląda twoja implementacja bool collision(TImage * a, TShape * b)?
Zależnie jakich funkcji używasz przy implementacji tej funkcji, problem staje się prosty lub skomplikowany.
Albo można użyć klasy bazowej, albo należy skorzystać z double dispatch.
Używanie dynamic_cast (TypeOf(a)) do sprawdzania typu to najgorsze rozwiązanie.

0

Już wstawiam i wyjaśniam. Z klas TImage i TShape potrzebuje wyciągnąć tylko pozycje na ekranie. w Obydwu przypadkach odwołuje się tak samo Klasa->Left i Klasa->Top tylko to są różne funkcje i aktualnie mam stworzone dwie funkcje z skopiowanym kodem identycznie różniące się tylko pierwszym parametrem.

bool collision(TShape * a, TShape * b){

    // left, right, top, bottom
	float al, ar, at, ab;
	float bl, br, bt, bb;

    al = a->Left;
	ar = a->Left + a->Width;
	at = a->Top;
	ab = a->Top  + a->Height;

	bl = b->Left;
	br = b->Left + b->Width;
	bt = b->Top;
	bb = b->Top + b->Height;



	// prawy bok a  z lewym bokiem b   && dół  a z góra b
	if(ar >= bl && ar <= br && ab >= bt && ab <= bb)
		return true;


    // prawy bok b z lewym bokiem a && dół b z górą a
	if(br >= al && br <= ar && bb >= at && bb <= ab)
		return true;


    return false;

}

Druga funkcja jest identyczna tylko za pierwszy argument przyjmuje TImage. tylko nie widzę sensu tworzenia dwoch takich samych funkcji z dokładnie taką samą zawartoscią.
Z dokumentacji widzę że obydwie klasy dziedziczą po

Vcl::Controls::TGraphicControl

za każdą cenną poradę, wskazówki będę bardzo wdzięczny, dopiero zaczynam z c++ :)

à propos tej funkcji, Taki kod rozkminiłem na sprawdzanie kolizji, niby działa tak jak chce ale jeśli macie pomysł jak to prościej zrobić to chetnie wysłucham. ( nie sugerowac się komentarzami przy ifach bo przerabialem )

4

Jako, że używasz tylko właśiwości, kóre występują w TControl to wystarczy ci ta klasa bazowa.
Zresztą jak patrzę jak to napisałeś to strasznie sobie utrudniasz, to jest już gotowe, trzeba tylko przeszukać dokumentację:

bool collision(TControl * a, TControl * b){
    return a->BoundsRect.IntersectsWith(b->BoundsRect);
}

https://docwiki.embarcadero.com/Libraries/Alexandria/en/System.Types.TRect.IntersectsWith
https://docwiki.embarcadero.com/Libraries/Alexandria/en/Vcl.Controls.TControl.BoundsRect

1

Muszę szybko nauczyć się korzystać z dokumentacji, niedośc że skróciłeś mi funckje do 1 linijki to już nie muszę kombinować z różnymi parametrami :)

0

Nie chcę zakładać nowego tematu więc napiszę tu
Chciałem z tablic przejść na wektory przy obsłudze strzałów ale już na samym początku napotkałem problemy.

vector<TShape*> Bullet;

void __fastcall TGame::createBullet(float bx, float by) {
	Bullet.emplace_back(new TShape(this));
	Bullet.end()->Parent 		= this;
	Bullet.end()->Visible 		= false;
	Bullet.end()->Width			= 10;
	Bullet.end()->Height 		= 5;
	bullet.end()->Left 			= bx;
	bullet.end()->Top  			= by
	Bullet.end()->Brush->Color  = clBlack;
}

już nie mam pomysłów

[bcc32c Error] Unit1.cpp(42): member reference base type 'std::_Simple_types<Vcl::Extctrls::TShape *>::value_type' (aka 'Vcl::Extctrls::TShape *') is not a structure or union
1
  1. Bullet.end() wskazuje poza zakres. To jest iterator do pierwszego nieistniejącego elementu!
  2. Bullet.end() to iterator do wskaźnika, żeby się skompilowało powinno być (*Bullet.end())->Parent.
  3. Chciałeś pewnie użyć Bullet.back()
0

Okej śmiga, nawet strzela xd

Tylko zastanawiam się czy wektory to dobry pomysł na to. Wcześniej jak miałem zrobione to na tablicy o wielkosci 200 to strzelalem i nie lagowalo tak jak teraz przy np 50 kulach na ekranie. Chyba że coś źle robię ? Nie oczekuję gotowca tylko nakierowania w czym tkwi problem :)

to it--;

bool shot = false;
vector<TShape*> Bullet;

void __fastcall TGame::createBullet() {
	shot = false;

	TShape *nb = new TShape(this);
	nb->Parent 	  = this;
	nb->Visible 	  = true;
	nb->Width	  = 10;
	nb->Height 	  = 5;
	nb->Left          = Player->Left + Player->Width - 25;
	nb->Top           = Player->Top + 25;
	nb->Brush->Color  = clBlack;

	Bullet.push_back(nb);
}

void __fastcall TGame::TimerBulletTimer(TObject *Sender){

	if(gameOver) return;

	if(shot)	createBullet();

	for(auto it = Bullet.begin(); it != Bullet.end(); it++){

       	(*it)->Left += 5;

		if((*it)->Left  > Game->Width){
			Bullet.erase(it);
            it--; // dodałem bo wywalało gre(chwile to zajeło zanim zatrybiłem) i nie wiem czy to dobrze zrobilem ?  XD
		}
	}

}
1

Każdemu new odpowiada delete, którego u ciebie nie ma. Dalej to poczytaj https://dsp.krzaq.cc/post/176/ucze-sie-cxx-kiedy-uzywac-new-i-delete/ i zaczynij używać unique_ptr

Co do erase to poczytaj dlaczego trzeba "przepniać" it, bo programowanie na ślepo to nie jest dobry pomysł

Jeśli w tej pętli usuwasz dużo pocisków na raz to pamiętaj, że usunięcie czegoś ze środka vectora polega na tym, że wszystko co jest po prawej stronie musi być przesunięte.

0

czyli zamiast new mam użyć unique_ptr

TShape *nb = new TShape(this)

na

unique_ptr<TShape> nb = make_unique<TShape>(this);

ale w takim wypadku wyrzuca mi błąd

[bcc32c Error] Unit1.cpp(52): no matching member function for call to 'push_back'
vector(964): candidate function not viable: no known conversion from 'unique_ptrVcl::Extctrls::TShape' to 'std::vector<Vcl::TShape *, std::allocator<Vcl::TShape *> >::value_type' (aka 'Vcl::TShape *') for 1st argument
vector(1377): candidate function not viable: no known conversion from 'unique_ptrVcl::Extctrls::TShape' to 'const std::vector<Vcl::TShape *, std::allocator<Vcl::TShape *> >::value_type' (aka 'Vcl::TShape *const') for 1st argument

Nie bijcie, początki są trudne :(

2
std::vector<std::unique_ptr<TShape>> Bullet;
Bullet.emplace_back(std::make_unique<TShape>(this));
0

vector<unique_ptr<TShape>> Bullet;

    bool collision(TControl * a, TControl * b){
    	return a->BoundsRect.IntersectsWith(b->BoundsRect);
    }

    // gdzieś dalej 
	for(auto it = Bullet.begin(); it != Bullet.end(); it++){
		(*it)->Left += 5 ;

		if((*it)->Left  > Game->Width){
			Bullet.erase(it);
            it--;
		}

		for(int i=0; i<ENEMY_COUNT; i++){
			if(collision((*it), Enemy[i])){
				enemyHit(i, (*it));
				Bullet.erase(it);
				it--;
		   	}
	   	}

	}

I wyrzuca błąd

Unit1.cpp(35): candidate function not viable: no known conversion from 'std::_Simple_types<std::unique_ptr<Vcl::TShape, std::default_deleteVcl::Extctrls::TShape > >::value_type' (aka 'std::unique_ptr<Vcl::TShape, std::default_deleteVcl::Extctrls::TShape >') to 'Vcl::TControl *' for 1st argument

w lini

if(collision((*it), Enemy[i]))
enemyHit(i, (*it));

próbowałem już na wiele sposobów, nie wiem jak mam to przekazać do funkcji

1

unique_ptr to jest wrapper na goły wskaźnik, którego funkcja oczekuje. Masz dwie opcje. Albo przy wywołaniu funkcji użyjesz funkcji get na unique_ptr, albo zmienisz prototyp swojej funkcji tak, aby przyjmowała referencję do unique_ptr. Pamiętaj tylko aby pilnować długości życia takiego wskaźnika, tzn. by funkcja wywoływana była w tym samym wątku i tym samym scope'ie, a jeśli te wskaźniki są w tablicy, to żeby inny kod w międzyczasie ich nie usunął.

Zatem powinieneś (zakładam opcję nr 1) wywołać te funkcje tak:

if(collision((*it).get(), Enemy[i]) //Enemy zawiera gołe wskaźniki czy unique_ptr? Jeśli to drugie, to też użyj get()
enemyHit(i, (*it).get());
0

Faktycznie, teraz działa... kolega @slsy wysyłał linka do kursu i tam wyraźnie była użyta funkcja get(), tylko ja pomyślałem że to jakaś przykładowa funkcja... czemu ? nie wiem choć się domyślam xD

mógłbyś mi podać jakby to wyglądało w przypadku Referencji ?

0
Starmi napisał(a):

mógłbyś mi podać jakby to wyglądało w przypadku Referencji ?

Coś takiego:

bool collision(const std::unique_ptr<TControl>& a, const std::unique_ptr<TControl>& b){
    	return a->BoundsRect.IntersectsWith(b->BoundsRect);
    }

Tylko pamiętaj, że kod wywołujący tę funkcję musi być właścicielem tego wskaźnika tak, żeby nie mogła wydarzyć się sytuacja, że zostaną one zniszczone podczas jej wykonywania.

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