Como posso criar esse template?

1. Como posso criar esse template?

Samuel Leonardo
SamL

(usa XUbuntu)

Enviado em 10/01/2023 - 12:01h

Alguém (humano) sabe como posso criar um template assim:
1-quero que um shared_ptr guarde o objeto alocado com new
2-mas quero construir objetos de qualquer tipo e guardar no shared_ptr
Exemplo:
auto sprite = addComponent<Sprite>(entity, paramSprite1, paramSprite2, paramSprite3);
Os paramSprite* são parametros para construção do objeto sprite.

Eu não estou conseguindo pensar num jeito do shared_ptr ter um tipo fixo, o compilador tá exigindo de mim que ele seja de um tipo fixo mas não é que quero.
Eu quero um shared_ptr pra qualquer tipo passado na template addComponent. Ali no exemplo é o tipo Sprite.

Tentei usar shared_ptr<void> mas não é aceito porque void * não existe nos smart pointer, o que acho um absurdo diga-se de passagem.

Então, como criar um shared_ptr com um ponteiro "universal"?

Uso c++ 17.


https://nerdki.blogspot.com/ acessa ai, é grátis
Não gostou? O ícone da casinha é serventia do site!


  


2. Re: Como posso criar esse template?

Samuel Leonardo
SamL

(usa XUbuntu)

Enviado em 10/01/2023 - 12:32h

#ifndef ECS_HPP
#define ECS_HPP

#include <vector>
#include <memory>
#include <unordered_map>

template<class T>
struct Component {
std::shared_ptr<T> component;
};

struct ECS {
private:
//formato <int=entidade, componentesEntidade>
std::unordered_map<int, std::vector<Component>> entities;
public:
template <typename T>
T* get (int id) {
if (id == 0)
return nullptr;

int i = 0;
for (auto it: entities[id]) {
auto ptr = reinterpret_cast<T*>(it.component.get());
if (ptr)
return ptr;
}
return nullptr;
}

bool has (int id) {
return entities.find(id) != entities.end();
}

template<typename T, typename ... Args>
void emplace(int id, Args ... args) {
if (id <= 0)
return;
std::shared_ptr<T> ptr(new T(args...));
entities[id].push_back(std::move(ptr));
}

int create() {
entities[entities.size() + 1];
return entities.size();
}

template <class T>
void some(std::function<bool(T *)> func) {
for (auto it: entities) {
for (auto comp: it.second) {
auto ptr = std::any_cast<T*>(comp.get());
if (ptr && func(ptr))
return;
}
}
}

template <class T>
void each(std::function<void(T *)> func) {
for (auto it: entities) {
for (auto comp: it.second) {
auto ptr = std::any_cast<T*>(comp.get());
if (ptr) {
func(ptr);
}
}
}
}

void destroy(int id) {
if (has(id)) {
entities.erase(id);
}
}
};

#endif


//regsitry:
struct RegistryStruct {
ECS * ecs;
int id = 0;

RegistryStruct() {
ecs = nullptr;
id = 0;
}
RegistryStruct(ECS *_ecs, int _id): ecs(_ecs), id(_id) {

}

void print () {
std::cout<<"RegistryStruct: ecs = "<<ecs<<" id = "<<id<<"\n";
}
};


template<typename T>
bool hasComponent(RegistryStruct &reg, bool throwOnError = true) {
return reg.ecs && reg.ecs->has(reg.id) && reg.ecs->template get<T>();
}


template<class T>
T * getComponent(RegistryStruct &reg, bool throwOnError = true) {
if (!hasComponent<T>(reg)) {
if (throwOnError)
std::runtime_error(std::string("Não Existe ")+typeid(T).name());
return nullptr;
}

auto it = reg.ecs->template get<T>(reg.id);
return (it);
}

template<typename T>
T * addComponent(RegistryStruct &reg) {
if (!reg.ecs->has(reg.id)) {
throw std::runtime_error("Entity é nula!");
}
auto entity = reg.id;
(reg.ecs->template emplace<T>(entity));
return const_cast<T*>(reg.ecs->get<T>(entity));
}

template<typename T, typename ... Args>
T * addComponent(RegistryStruct &reg, Args && ... args) {
if (!reg.ecs->has(reg.id)) {
throw std::runtime_error("Entity é nula!");
}
auto entity = reg.id;
reg.ecs->emplace<T, Args...>(entity, args...);
return const_cast<T *>(reg.ecs->template get<T>(reg.id));
}

Fica melhor com um exemplo:
1-ali é minha classe de ECS (quebrada)
2-uso funções templates pra adicionar ou remover compoents.
3-cada component é de um tipo diferente, logo, não é possível ter um map tipo <int,Component<Sprite>> sendo que não existe, ainda, o tipo Sprite.
4-exemplo de uso:
-crio uma classe chamada Sprite
-adiciono um component spirte a um RegistryStruct assim:
auto reg = ecs->create();
//adiciona um componente Sprite ao reg
auto sprite = addComponent<Sprite>(reg, param1, param2, param3);

O meu problema é que não sei usar template com shared_ptr pra ter um ponteiro que varie de acordo com o tipo passado e então guarde ele numa struct Component.

Alguma ideia do que fazer pra ter uma solução mais elegante?


3. Re: Como posso criar esse template?

leandro peçanha scardua
leandropscardua

(usa Ubuntu)

Enviado em 10/01/2023 - 15:10h


Pela descrição acho q vc quer uma "abstract factory" procure por padrões de projeto orientados a objetos.


4. Re: Como posso criar esse template?

Samuel Leonardo
SamL

(usa XUbuntu)

Enviado em 10/01/2023 - 17:03h


leandropscardua escreveu:


Pela descrição acho q vc quer uma "abstract factory" procure por padrões de projeto orientados a objetos.

Valeu a dica Leandro, mas não estou tendo problema na criação do objeto em si, mas sim no armazenamento do mesmo num shared_ptr.
Oor exemplo, em C++ void *p aponta pra qualquer coisa na memória. É um jeito esquisito de ter um ponteiro universal.
O problema é que, smart pointers não aceitam void * como tipo. Não lembro exatamente o porquê mas penso ser por falta de destrutor/construtor pra tal tipo.

No fim, inventyaram uma "gambiarra" chamada std::any, que pessoalmente acho uma baita redundância pois já existe void * na linguagem.

Enfim, armazenar o ponteiro criado por ECS->emplace tá mais difícil do que criar uma classe base e derivar as outras dessa classe.

https://nerdki.blogspot.com/ acessa ai, é grátis
Não gostou? O ícone da casinha é serventia do site!


5. Re: Como posso criar esse template?

Buckminster
Buckminster

(usa Debian)

Enviado em 10/01/2023 - 21:57h

Criar instâncias de shared-ptr:
https://learn.microsoft.com/pt-br/cpp/cpp/how-to-create-and-use-shared-ptr-instances?view=msvc-170

Ponteiros Inteligentes:
https://learn.microsoft.com/pt-br/cpp/cpp/smart-pointers-modern-cpp?view=msvc-170

"Declare o ponteiro inteligente como uma variável automática (local). (Não use a expressão new ou malloc no ponteiro inteligente propriamente dito.)

No parâmetro de tipo, especifique o tipo apontado do ponteiro encapsulado.

Passe um ponteiro bruto para um objeto new no construtor do ponteiro inteligente. (Algumas funções do utilitário ou construtores de ponteiro inteligente fazem isso para você.)

Use os operadores -> e * sobrecarregados para acessar o objeto.

Deixe o ponteiro inteligente excluir o objeto."

"weak_ptr
Ponteiro inteligente de casos especiais para uso em conjunto com shared_ptr. Um weak_ptr fornece acesso a um objeto pertencente a uma ou mais instâncias de shared_ptr, mas não participa da contagem de referência. Use quando você quiser observar um objeto, mas sem exigir que ele permaneça ativo. Necessário em alguns casos para interromper referências circulares entre instâncias shared_ptr. Arquivo de cabeçalho: <memory>. Para obter mais informações, confira Como criar e usar instâncias weak_ptr e Classe weak_ptr."

https://en.cppreference.com/w/cpp/memory/weak_ptr

https://linuxhint.com/practical-tutorial-on-the-use-of-weak-pointers-how-they-work-and-the-purpose-o...


________________________________________________
Always listen the Buck!


6. Re: Como posso criar esse template?

Samuel Leonardo
SamL

(usa XUbuntu)

Enviado em 11/01/2023 - 07:04h

Resolvido. Revi todo o código e adicionei uma classe base a todos os componetes. E ai, basta alocar o componente e guardar o ponteiro pra classe base no shared_ptr.

Não é bem o que eu queria mas já tava ficando doido de tentar outro modo mágico.


https://nerdki.blogspot.com/ acessa ai, é grátis
Não gostou? O ícone da casinha é serventia do site!






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts