Writing a C ++ Interface for a Dynamically Distributed C Structure

Introduction: I am writing a C ++ 11 application that makes extensive use of the legacy C code base. A very common pattern in legacy code is the existence of some struct LegacyStructthat are created and destroyed by methods such as

build_struct(LegacyStruct *L, int arg1, int arg2)
free_struct(LegacyStruct *L)

which are mostly constructors / destructors. The ownership model in the legacy codebase is very similar to unique_ptr-esque, so I am aiming to port it to the secure RAII-minded class for the shell, as shown below:

class Wrapper {
public:
    Wrapper::Wraper() : handle() {}
    Wrapper::Wrapper(int same_arg1, int same_arg2);
    Wrapper::Wrapper(const Wrapper &W) = delete;
    Wrapper::Wrapper(Wrapper &&W) : handle(std::move(W.handle)) {}
    //copy operator= and move operator= analogously
private:
    std::unique_ptr<LegacyStruct, custom_deleter> handle;

custom_deleter free_struct std::default_delete LegacyStruct. , , , , .

: ,

typedef struct LegacyNode {
    int stack_allocated_data;
    OtherStruct *heap_allocated_data;
    LegacyNode *next;
} LegacyNode;

, unique_ptr -esque: , . , free_node(LegacyNode *N), heap_allocated_data, node.

. ,

build_list(LegacyNode **L, int *count_p, int other_args){
    LegacyNode *newnode;

    //code allocating newnode and populating its fields

    //...and then:
    newcut->next = *L;
    *L = newcut;
    (*count_p)++;
}

build_list

int list_count = 0;
LegacyNode *L = (LegacyNode *) NULL;

build_list(&L, &list_count, 99);

/: build_list - , , , , build_list, , .

, ListWrap, node , /, Wrapper , .. , , ..

, , . head_node LegacyNode &head_node.get() build_list, / ?

- node, , node build_list, , , free_node erase , .

, - CS-101, ! , , , , - .

+4
2

, , . head_node LegacyNode &head_node.get() build_list, / ?

, , build_list , . : std::unique_ptr !

, ListWrap build_list , RAII.

class ListWrap {
public:
    ListWrap(LegacyNode* head, int count);
    //...
private:
    std::unique_ptr<LegacyNode, &free_node> handle;
    int count;
};

ListWrap::ListWrap(LegacyNode* head, int count) : handle{ head }, count{ count } {}
+2

:

struct Nodes {
  struct DeleteAllNodes {
    void operator()(LegacyNode* node)const {
      while (auto cur = node) {
        node = cur->next;
        free_node(node);
      }
    }
  };
  std::unique_ptr<LegacyNode, DeleteAllNodes> m_nodes;
};

. , , :

void push_node( Nodes& nodes, int other_args ) {
  int unused = 0;
  auto* tmp = nodes.m_nodes.get();
  build_list( &tmp, &unused, other_args );
  nodes.m_nodes.release(); // unmanaged
  nodes.m_nodes.reset(tmp); // everything managed now
}
Nodes pop_node( Nodes& nodes ) {
  if (!nodes.m_nodes) return {};
  auto* tmp = nodes.m_nodes->next; // unmanaged
  nodes.m_nodes->next = nullptr;
  Nodes retval;
  retval.m_nodes.reset(tmp); // everything managed now
  std::swap( retval.m_nodes, nodes.m_nodes );
  return retval;
}
void move_single_node( Nodes& dest, Nodes& src ) {
  Assert(src.m_nodes);
  if (!src.m_nodes) return;
  Nodes to_push = pop_node(src);
  LegacyNode** next = &(to_push.m_nodes->next);
  Assert(!*next); // shouldn't be possible, pop_node returns a single node
  *next = dest.m_nodes.release(); // unmanaged for a short period
  dest = std::move(to_push);
}
Nodes splice( Nodes backwards, Nodes forwards ) {
  while(backwards.m_nodes) {
    move_single_node( forwards, backwards );
  }
  return forwards;
}
template<class F>
void erase_if( Nodes& nodes, F&& f, Nodes prefix={} ) {
  if (!nodes.m_nodes) {
    return splice( std::move(prefix), std::move(nodes) );
  }
  Nodes tmp = pop_node( nodes );
  if ( !f(*tmp.m_nodes) ) {
    prefix = splice( std::move(tmp), prefix );
  }
  erase_if( nodes, std::forward<F>(f), std::move(prefix) );
}

, Nodes& , Nodes.

+1

Source: https://habr.com/ru/post/1663018/


All Articles