Search code examples
c++structdynamic-arrays

Return struct of structs with dynamic struct arrays


I have a struct containing dynamic struct arrays:

struct C_Node {
    int id;
    int num_children;
    int* children;
};

struct C_Transition {
    int id;
    int duration;
    int num_parents;
    int num_children;
    int* parents;
    int* children;
};

struct C_PetriNet {
    int num_nodes;
    int num_transitions;

    C_Node* nodes;
    C_Transition* transitions;
};

I want to initialize and return the outer struct like the following:

C_PetriNet* Cpp_C_interface::convert_PetriNet(PetriNet petriNet) {
    int num_nodes = static_cast<int>(petriNet.nodes.size());
    int num_transitions = static_cast<int>(petriNet.transitions.size());

    C_PetriNet* c_petriNet = (C_PetriNet*)malloc(sizeof(C_PetriNet));
    C_Node* c_nodes = new C_Node[num_nodes];
    C_Transition* c_transitions = new C_Transition[num_transitions];

    for (int i = 0; i < num_nodes; i++) {
        c_nodes[i].id = petriNet.nodes[i].id;
        c_nodes[i].num_children = petriNet.nodes[i].childs.size();
        c_nodes[i].children = petriNet.nodes[i].childs.data();
    }

    for (int i = 0; i < num_transitions; i++) {
        c_transitions[i].id = petriNet.transitions[i].id;
        c_transitions[i].duration = petriNet.transitions[i].duration;
        c_transitions[i].num_parents = petriNet.transitions[i].parents.size();
        c_transitions[i].num_children = petriNet.transitions[i].childs.size();
        c_transitions[i].children = petriNet.transitions[i].childs.data();
        c_transitions[i].parents = petriNet.transitions[i].parents.data();
    }

    c_petriNet->num_nodes = num_nodes;
    c_petriNet->num_transitions = num_transitions;
    c_petriNet->nodes = c_nodes;
    c_petriNet->transitions = c_transitions;

    return c_petriNet;
};

And use it in the main:

C_PetriNet* c_petriNet;
c_petriNet = Cpp_C_interface::convert_PetriNet(petriNet);
std::cout << "Test out: " << c_petriNet->num_nodes << std::endl;
std::cout << "Test out: " << c_petriNet->nodes[5].children[8] << std::endl;
std::cout << "Test out: " << c_petriNet->transitions[68].parents[1] << std::endl;

However, only the first output (num_nodes) is correct. If I print inside the function before returning, everything works fine. What can I do to return also the dynamic allocated memory?


Solution

  • The problem is that petriNet is a local copy of the object that was passed to your function. You're saving pointers to the data() of various vectors in petriNet in the new C_Node and C_Transition that you're creating, but these pointers become invalid when the function returns.

    If you change your function to take a reference, the pointers will remain valid as long as the caller's object is alive, but that's still fragile. What you really need to do is make copies of all the data. So you can use memcpy():

    memcpy(c_nodes[i].children, petriNet.nodes[i].childs.data(), c_nodes[i].num_children * sizeof(*petriNet.nodes[i].childs.data());