Passer un shared_ptr à travers une interface C qui prend vide *

J’ai un projet C ++ qui utilise SDL , en particulier SDL Events. J’aimerais utiliser le système d’événements pour les messages réseau entrants, tout comme pour les événements d’interface utilisateur. Je peux définir un nouveau type d’événement et attacher des données arbitraires (voir cet exemple ). Voici ce que je ferais si j’utilisais des pointeurs ordinaires:

Uint32 message_event_type = SDL_RegisterEvents(1); /* In the main event loop */ while (SDL_Poll(&evt)) { if (evt.type == message_event_type) { Message *msg = evt.user.data1; handle_message(msg); } } /* Networking code, possibly in another thread */ Message *msg = read_message_from_network(); SDL_Event evt; evt.type = message_event_type; evt.user.data1 = msg; SDL_PostEvent(evt); 

Au lieu de cela, j’ai utilisé shared_ptr jusqu’à présent. Les messages sont des objects en lecture seule une fois construits, et peuvent être utilisés à de nombreux endroits tout en étant traités. J’ai donc pensé à utiliser shared_ptr pour eux.

Je voudrais utiliser un shared_ptr pour le message du côté du réseau, et aussi du côté de la gestion des événements. Si je fais ça:

 // in networking code: shared_ptr msg = ... evt.user.data1 = msg.get(); // later, in event handling: shared_ptr msg(evt.user.data1); 

alors il y a deux shared_ptrs indépendants et l’un ou l’autre pourrait supprimer l’object Message alors que l’un ou l’autre l’utilise encore. Il me faudrait en quelque sorte passer le shared_ptr via la structure SDL_UserEvent, qui ne contient que deux champs void * et int.

Additionnel. Notez que SDL_PostEvent renvoyé immédiatement. l’événement lui-même est mis en queue. Le gestionnaire peut extraire l’événement de la file d’attente bien après l’extension du champ shared_ptr du message dans le code de réseau. Je ne peux donc pas transmettre l’adresse d’un shared_ptr local à copier. Au moment où la copie est effectuée, il est probable qu’elle ne soit plus valide.

Quelqu’un a-t-il rencontré un problème similaire et connaît-il une solution intéressante?

Atsortingbuez un pointeur au ptr partagé avec new . Ceci appelle le constructeur (incrémentant le compte de références), mais le destructeur correspondant n’est pas appelé, ainsi le shared_ptr ne détruira jamais sa mémoire partagée.

Ensuite, dans le gestionnaire correspondant, détruisez simplement l’object après avoir copié le shared_ptr, ramenant son compte de références à la normale.

Ceci est identique à la manière dont vous passez tout autre type non primitif dans une queue de messages.

 typedef shared_ptr MessagePtr; Uint32 message_event_type = SDL_RegisterEvents(1); /* In the main event loop */ while (SDL_Poll(&evt)) { if (evt.type == message_event_type) { // Might need to cast data1 to (shared_ptr *) unique_ptr data (evt.user.data1); MessagePtr msg = *data; handle_message(msg); } } /* Networking code, possibly in another thread */ MessagePtr msg = read_message_from_network(); SDL_Event evt; evt.type = message_event_type; evt.user.data1 = new MessagePtr (msg); SDL_PostEvent(evt); 

Les messages sont des objects en lecture seule une fois construits

Je tiens simplement à souligner que ceci est bon et même nécessaire pour que le multithreading soit sûr. Vous voudrez peut-être utiliser shared_ptr

Semble un endroit idéal pour utiliser std::enable_shared_from_this

 struct Message: std::enable_shared_from_this { … }; evt.user.data1 = msg.get(); // this msg uses the same refcount as msg above shared_ptr msg = evt.user.data1.shared_from_this(); 

J’ai pensé à une autre technique potentielle: utiliser placement new pour stocker le shared_ptr dans le même espace que la structure C:

 SDL_Event evt; evt.type = event_type; // create new shared_ptr, in the same memory as evt.user.code new (&evt.user.code) shared_ptr(msg); SDL_PushEvent(&evt); 

SDL copie ensuite l’événement en tant qu’object C, jusqu’à ce que le code extrait ultérieurement le message de l’événement:

 shared_ptr get_message(SDL_Event& evt) { // copy shared_ptr out of evt shared_ptr msg = *reinterpret_cast *>(&evt.user.code); // destroy shared_ptr inside the event struct (reinterpret_cast *>(&evt.user.code))->~shared_ptr(); return msg; } 

Il y a plusieurs champs dans la structure d’événement qui devraient être suffisamment d’espace pour shared_ptr (voir https://github.com/spurious/SDL-mirror/blob/master/include/SDL_events.h#L485 ).

Je suis conscient que c’est un peu hacky. J’apprécierais un peu de vérification de la technique.