6 votes

Pourquoi mes données de résultat retournées en tant que void* se cassent-elles ?

Je travaille sur un projet qui comporte une énorme base de code héritée et j'ai essayé d'en reconcevoir certaines parties pour m'éloigner de l'ancien code de style c. Je n'ai pas pu m'empêcher d'en parler.

J'ai rencontré un problème et j'ai préparé un petit programme pour l'expliquer.

L'ancienne interface que j'utilise me demande de passer un pointeur vers les données de résultat en tant que void* et je voudrais éviter de devoir modifier cela.

L'unique_ptr dans l'exemple sert juste à démontrer que, dans ma base de code réelle, tout ce qui travaille sur les données utilise des pointeurs intelligents pour gérer la mémoire.

Mon problème est que les données de résultat sont brisées (voir la dernière ligne de sortie / dernier appel à printPayload) ; tout est à 0 à la fin, mais il ne semble pas y avoir de problème avec la conversion en void* et le retour, comme le montrent les 2e et 3e lignes de sortie.

S'agit-il d'un problème lié aux temporaires ? Je ne comprends pas...

J'espère que ce type de problème est pertinent pour certains d'entre vous.

#include <iostream>
#include <memory>

struct Payload
{
    long a;
    int b;
    int c;

    Payload() : a(), b(), c() {}
    Payload(long setA, int setB, int setC) : a(setA), b(setB), c(setC) {}
};

void printPayload(const Payload& printThis)
{
    std::cout << "payload -- a: " << printThis.a << " b: " << printThis.b << " c: " << printThis.c << std::endl;
}

void doSomething(Payload* sourceData, void* targetData)
{
    if (!sourceData) return;

    std::unique_ptr<Payload> sourceDataUnique(sourceData);

    sourceDataUnique->a = 222;
    sourceDataUnique->b = 333;
    sourceDataUnique->c = 444;

    printPayload(*sourceDataUnique);

    targetData = reinterpret_cast<void*>(sourceDataUnique.release());

    printPayload(*(reinterpret_cast<Payload*>(targetData)));
}

int main(void)
{
    Payload* myPayload = new Payload(14, 8, 1982);
    Payload myResult;

    printPayload(*myPayload);

    doSomething(myPayload, &myResult);

    printPayload(myResult);
}

Sortie :

payload -- a: 14 b: 8 c: 1982
payload -- a: 222 b: 333 c: 444
payload -- a: 222 b: 333 c: 444
payload -- a: 0 b: 0 c: 0

8voto

StoryTeller Points 6139

targetData est un local à la variable doSomething . Une fois que vous lui avez attribué une adresse, il sort du champ d'application.

Vous n'attribuez jamais réellement à myResult .

3voto

Daniele Points 945

Dans votre code, le paramètre targetData est local à doSomething (c'est-à-dire que toute modification est perdue après avoir quitté la portée de la fonction). Toutefois, *targetData fait référence à la variable myResult déclaré dans main fonction.

Ainsi, le code suivant devrait fonctionner :

void doSomething(Payload* sourceData, void* targetData)
{
    if (!sourceData) return;

    sourceData->a = 222;
    sourceData->b = 333;
    sourceData->c = 444;

    printPayload(*sourceData);

    Payload* td = static_cast<Payload*>(targetData);
    *td = *sourceData;
    printPayload(*td);
}

1voto

Pete Kirkham Points 32484

Vous ne copiez jamais les données source vers la cible, mais vous changez seulement l'objet vers lequel pointe le pointeur targetData.

Quelque chose comme ça pourrait fonctionner :

Payload* targetPayload = reinterpret_cast<Payload*>(targetData);
*targetPayload = *sourceData;

Prendre la propriété de la charge utile source via un pointeur intelligent est probablement une mauvaise idée - si le code appelant est écrit pour gérer les exceptions correctement, il supprimera l'objet en cas d'erreur, donc le pointeur intelligent signifiera qu'il sera supprimé deux fois. Si le code appelant n'est pas écrit pour gérer les exceptions correctement, alors c'est votre travail d'écrire du code qui ne peut pas lancer une exception, ce que le pointeur intelligent n'aide pas à faire.

(comme c'est un cast entre pointeurs, vous pourriez utiliser static_cast, mais je préfère reinterpret_cast car le void* pourrait être tout ce qui est et reinterpret_cast indique aux autres développeurs que quelque chose de potentiellement dangereux est en train de se produire).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X