Channels ▼
RSS

C/C++

Building Your Own Plugin Framework: Part 4


C++ Object Model Wrappers

This is a little weird but necessary. You take a perfectly good dual C/C++ object that you have access only to its C interface and then you wrap it in a C++ wrapper with the same interface. The wrapper can be lean or fat, especially when it comes to iterators. The wrapper can keep the C iterator and call it in response to next() and reset() calls or it can copy the entire collection.

For the sample game I chose the second approach. It is a little more expansive at call time, but if you use the same data again and again then it can actually be faster because you don't have to wrap the result of each iteration (if you iterate multiple times).

Listing One presents the object model wrappers for the demo game.

#ifndef OBJECT_MODEL_WRAPPERS_H
#define OBJECT_MODEL_WRAPPERS_H

#include <string>
#include <vector>
#include <map>

#include "object_model.h"
#include "c_object_model.h"

struct ActorInfoIteratorWrapper : public IActorInfoIterator
{
  ActorInfoIteratorWrapper(C_ActorInfoIterator * iter) : index_(0)
  {
    iter->reset(iter->handle);
    
    // Create an internal vector of ActorInfo objects
    const ActorInfo * ai = NULL;
    while ((ai = iter->next(iter->handle)))
      vec_.push_back(*ai);
  }
    // IActorInfoIteraotr methods
  virtual void reset()
  {
    index_ = 0;
  }
  virtual ActorInfo * next()
  {
    if (index_ == vec_.size())
      return NULL;
      
    return &vec_[index_++];
  }
private:
  apr_uint32_t index_;
  std::vector<ActorInfo> vec_;
};
struct TurnWrapper : public ITurn
{
  TurnWrapper(C_Turn * turn) : 
    turn_(turn),
    friends_(turn->getFriends(turn->handle)),
    foes_(turn->getFoes(turn->handle))
  { 
  }
  
  // ITurn methods
  virtual ActorInfo * getSelfInfo()
  {
    return turn_->getSelfInfo(turn_->handle);
  }
  virtual IActorInfoIterator * getFriends()
  {
    return &friends_;
  }
  virtual IActorInfoIterator * getFoes()
  {
    return &foes_;
  }
  virtual void move(apr_uint32_t x, apr_uint32_t y)
  {
    turn_->move(turn_->handle, x, y);
  }
  virtual void attack(apr_uint32_t id)
  {
    turn_->attack(turn_->handle, id);
  }

private:
  C_Turn * turn_;
  ActorInfoIteratorWrapper friends_;
  ActorInfoIteratorWrapper foes_;
};
#endif // OBJECT_MODEL_WRAPPERS_H
Listing One

Note that I need to wrap the C interfaces of any object passed to the main interface C_Actor, as well as any object passed to its arguments recursively. Luckily (or by design), there aren't too many objects that need to be wrapped. The ActorInfo struct is common to both the C and C++ interfaces and needs no wrapping. The other objects are the C_Turn object and the C_ActorInfoIterator objects. These objects are wrapped by the ActorInfoIteratorWrapper and TurnWrapper correspondingly. The implementation of wrapper objects is usually pretty simple, but if you have a large number of them it can be tiresome and a maintainance headache. Each wrapper derives from the C++ interface and accepts the correponding C interface pointer in its constructor. For example, the TurnWrapper object derives from the C++ ITurn interface and accepts the a C_Turn pointer in its constructor. Wrapper objects store their C interface pointer and in the implementation of their methods they usually forward the call to wrapped object via the stored C interface pointer and wrap the result on-the-fly if necessary. In this case ActorInfoIteratorWrapper takes a different approach. In its constructor it iterates over the passed in C_ActorInfoIterator and stores the ActorInfo objects in an internal vector. Later in its next() and reset() methods it just works with its populated vector. That wouldn't work, of course, if the collection the iterator works with can be modified after construction. This is fine because all the ActorInfo collection passed in are immutable. But, it is something to consider and you need to understand your object model and how it is supposed to be used to design intelligent wrappers. The TurnWrapper is a little more conservative and forwards calls to getSelfInfo(),attack(), and move() to its stored C_Turn pointer. It takes a different approach with the getFoes() and getFriends() methods. It saves the friends and foes in ActorInfoIteratorWrapper data members that it simply returns from calls to getFriends() and getFoes(). The ActorInfoIteratorWrapper objects implement the IActorInfoIterator interface, of course, so they have the proper data type required by the C++ ITurn interface.

How bad is the performance hit?

It depends. Remember that you may wrap every C type in your objct model, but you don't have too. You may opt instead to use some C objects as is. The real overhead comes in if you pass deep nested data structures as arguments and you decide to wrap each and every one of them. This is exactly the choice I made in a recent project. I had a complicated data structure that involved several maps that contained vectors of some struct. I wasn't worried about the wrapping overhead because this complex data structure was used for initialization only.

The big issue here is if you want the caller to maintain ownership of the data or if you want to copy it and not worry about the memory management strategies of the caller and if the data is mutable or not (which will preclude storing a snapshot). These are general C++ design concerns and are not specific to the object model wrappers.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video