Gillius's Programming

GNE API Major Changes

Since the last public release of GNE (0.55) I have decided to make significant changes to the API.

I choice this for a few reasons:

To tell the truth, the only real huge change in the existing GNE code is the move to reference-counted pointers almost exclusively throughout the code. While it sounds simply stated it actually has a lot of effect on the calling code such that work is required to port to the new GNE -- not much -- but still enough I'd like to call it "major API change."

I'm thus writing this document which will be, more or less, your porting guide. I hope to document the changes here as best as my time allows. It is very possible I missed something out here, so please tell me if you find any mistakes in this document.

This is NOT a complete change guide but instead a guide to the major changes that require changes to your code. See the Changes file for added functionality and other changes.

Smart Pointers

In this new version of GNE I decided to make a move to using smart pointers. The current implementation that I have chosen is Boost's shared_ptr, which is a non-intrusive reference counted smart pointer. In the GNE API, the smart pointers manifest themselves as the GNE::SmartPtr class. Yes, I did find out later the popular Loki library also has a class "SmartPtr" but this is what namespaces are for.

Because SmartPtr is a reference counted pointer, that means when the last SmartPtr pointing to an object dissapears, the object is automatically deleted. Reference counting in this sense appears exactly like garbage collection except for one major flaw -- if an object directly or indirectly (through other classes) points to itself (this is called a cycle), then the object will never be deleted, causing a memory leak. The way to break cycles with boost::shared_ptr is to use boost::weak_ptr, which in GNE manifests itself as GNE::WeakPtr. Weak pointers "become empty" when no strong pointers point to that object -- in other words weak pointers to not keep the object from being destroyed.

Unfortunately this makes the generator-listener relationship difficult. To keep the same design as GNE 0.55 of registering listeners and then being able to "forget" about them, I resolve cycles by keeping a SmartPtr to the listener and manually resetting it when the generator generates its last event. Because Connection and Timer objects are automatically shut down when GNE is shutdown on program exit, no memory leaks will result. This means you can safely retain SmartPtr to GNE's objects in your listeners. I wanted to make the issue of cycles as transparent as possible to the end-developer so I place all of the burden of handling possible cycles with GNE.

Reference counting has a few key advantages over true garbage collection:

GNE enforces the use of smart pointers to its objects by making the constructors private and providing public static "create" methods that construct a new object and return a SmartPtr to it. By forcing objects to be referenced by a smart pointer from the very start, it prevents any accidental leaks, espically unexpected ones due to exceptions.

Interface Changes

Thread class:

ServerConnectionListener class:

Address class:

Connection and ConnectionEventGenerator class:

GNE Initialization and Shutdown:

Packet class

CustomPacket class

Deprecated Classes

RawPacket class

Deprecated Methods

PacketParser Namespace:

registerPacket( id, createFunc ) - this function has been deprecated, because just specifying a creation function was not enough. GNE assumed that the create function created a packet with the "new" operator, and that deleting it with the "delete" operator was acceptable. This has two drawbacks, the first being that the user couldn't use their own memory allocation scheme for packets, and the second being that the library was not able to be in a DLL.

While work on a DLL version of GNE has not started, this would have been a major flaw to overcome in that process, so switching makes GNE one step closer.

Now the user is required to supply a creation function, a clone function (replaces what "makeClone" did before), and a destroy function. GNE will call the destroy function rather than "delete" when it destroys a packet now.

However, specifying three functions now does not mean there is more work on the user. On the contary, templated default functions are now provided for creation using the new operator, cloning using the copy constructor, and deletion using the delete operator. Because of the way templates work, this still means the user's code, not GNE's code will be calling the new/delete operator of the user's C++ run-time and not GNE's.

So for users who are not interested in writing their own packet allocator (probably almost all of you), the implementation has now actually become a lot simpler. You no longer need to define a static "create" function or overload a virtual makeClone function, if you use the defaultRegisterPacket function. You do need to defined a "static const int ID" field for your class. Then once you've done that, you register it like so:

class MyPacket : public Packet {
public:
  MyPacket() : Packet( ID ) {}
  MyPacket( const MyPacket& o ) : Packet( o ) {}
  virtual ~MyPacket() {}

  static const int ID;
  //readPacket, writePacket, getSize methods...
};

const int MyPacket::ID = MIN_USER_ID;

//Then in main method:
GNE::PacketParser::defaultRegisterPacket<MyPacket>();

And that's all there is to it... The previous API did not require the copy ctor to be properly defined, but now it needs to. It is good practice to do so anyway.

Thread class:

Connection class:

ClientConnection class:

Console Namespace:

CustomPacket class: