GlobalSentry 3.0.1 | Library Tutorial

© 2007 Kevin P. Barry


Table of Contents

  1. Introduction.
    1. Overview.
    2. Installation.
    3. Compilation.

  2. Capsule Interfaces.
    1. Abstract Interface.
    2. Static Object Storage.
    3. Dynamic Object Storage.
    4. Integrated Object Storage.

  3. Capsule Access Patterns.
    1. Write Access.
    2. Read Access.
    3. Access Results.

  4. Independent Capsule Duplication.
    1. Dynamically-allocated Duplicates.
    2. Pre-allocated Duplicates.

  5. Unix-specific Access Functionality.
    1. Automatic Access Functions.
    2. Access Function Parameters.

  6. Skepticism and Notes.
    1. Immediate Access to Objects.
    2. Pointer-copying Capsule?
    3. dynamic_capsule Cloning.
    4. Change of Purpose.
    5. Change of Format.
    6. Change of Name.


  1. Introduction.

    The GlobalSentry library (previously the Capsule library) is a C++ template library used to protect objects while allowing easy transfer and copy. Its main purpose is to protect centralized program information which is to be made publicly available to multiple independent units of the same program.

    1. Overview. When writing a multi-unit program (multiple libraries, threads, etc.,) you will usually find that you must maintain some sort of centralized data common to all of the independent units. This often leads to difficult-to-trace bugs because of the limitless access points to the centralized data. Leaving data out in the open does little to remedy this, so many developers resort to creating their own wrapper classes to control access to it. This is often a burdensome task and can leave the wrapper class responsible for a vast amount of functionality.

      The GlobalSentry library provides the developer with simple access control by storing objects and forcing a specific access pattern. The encapsulating object, called a capsule for simplicity, limits access to the encapsulated object to modules which contain specific access points. The capsule limits write access to one module at a time and keeps track of the total number of modules with read access at any given time. Functions other than the access points in the modules do not have access to the encapsulated objects, but can freely copy the entire capsule.

      An incidental result of forcing access via a module is thread deadlock prevention. The encapsulating objects themselves manage their own mutexes, preventing outside functions from setting one and failing to unset it. This also makes it notably difficult to accidentally have a thread deadlock itself.

      Restricting access in this manner forces functions to deal with protected centralized data using modular processes, which prevents lengthy, procedural global functions. This makes bugs quite a bit easier to isolate and assures the program unit maintaining the centralized data that the global visibility of the object cannot be abused easily.

      A secondary feature of the provided objects is the ability to copy them without having access to their encapsulated objects. This can be used to make independent local copies of centralized objects, or to allow functions to copy objects to pass on without having access to them.

      For library authors, the combination of copyability and access limitations can allow them to enforce modular use of their library's objects by their users, or to allow the users to store and copy information trivial to all but the library itself. Rather than developing classes with no public interface, objects with normal public interfaces can be used to ease the library's internal use of them.

    2. Installation. Because this library has no non-template content, there is nothing to compile ahead of time and incidentally nothing to link to. To install this library, copy the ta0kira directory into your include path, or just copy the contents of the ta0kira directory into your project's source code directory for local use.

    3. Compilation. This library has no special compilation instructions. If you include the appropriate header file where the library is used then everything will be compiled into the corresponding object files.



  2. Capsule Interfaces.

    This library contains one abstract capsule base class and several implementation classes. Each of the implementation classes has a specific use and purpose as detailed in this section. All classes are contained in namespace protect and can be included with #include <ta0kira/global-sentry.hpp> after installation.

    1. Abstract Interface. The base class for all capsules is the capsule class. This class provides the public interfaces to copy and access any derived capsule. The single template parameter of this class defines the interface type of the encapsulated object. That interface will generally be an abstract class type, leaving the implementation up to the encapsulated object.

      In addition to the copy and access interfaces (discussed in their appropriate sections,) this class provides comparison operators as a convenience to library authors who would like to develop a "copy, store, and compare" pattern with objects not accessible by the library's users.

      Pointers to capsule should be the external interface displayed globally when protecting centralize objects. This prevents outside program units from accessing the public functions added by derived classes and keeps them unaware of the true storage type of the object. You should not store a capsule locally as a pointer to capsule. Use a dynamic_capsule locally if you would like to have dynamic control over the underlying type.

    2. Static Object Storage. The simplest way to store protected objects is to make them data members of a class. This library provides two such classes: literal_capsule and derivative_capsule. As the names imply, literal_capsule contains an object of the literal interface type of the capsule and derivative_capsule contains an object of a type derived from that interface type. For example:

      #include <ta0kira/global-sentry.hpp>

      class Abstract {};

      class Implementation : public Abstract {};

      int main()
      {
        protect::literal_capsule <Implementation> literal;
        protect::derivative_capsule <Abstract, Implementation> derivative;
      }

      In the example above, literal expresses the full interface of Implementation when it is accessed, whereas derivative only expresses the interface of Abstract when it is accessed. Both have an underlying object of type Implementation, however. When using abstract interfaces, derivative_capsule is more advantageous because it allows the underlying type to be any class derived from that interface while retaining type-transparency to the users of the capsule. literal_capsule has the advantage of allowing encapsulation of simple types such as plain old data.

      Both of these classes take an optional initializing argument. This argument is copied and stored and further access to the stored object is limited. These classes are therefore only intended for objects with simple initialization. Once initialized, the encapsulated object can be accessed as const via the local_access member function. This makes it quite a bit easier for the owning program unit to check for changes and other status.

      literal_capsule and derivative_capsule can both be used with fixed-size arrays. When using derivative_capsule, the interface type must be either an array of the same type and size (Type[Size]), or a Type *const of the same type.

      If there is any potential for one of these capsules to be destructed while a module is using it, you should condemn it to prevent further access and use a wait loop before the capsule goes out of scope:

      #include <ta0kira/global-sentry.hpp>

      int main()
      {
        protect::literal_capsule <int> integer;

        integer.condemn();
        while (integer.viewing_status()) /*wait X seconds*/;
      }

      The loop isn't built in to the destructor because of portability and developer preference (condemn is, however.) The viewing_status function returns true if any module is currently viewing or modifying the capsule. The condemn function restricts future access to the capsule (which cannot be undone) and also replaces the current pointers being used by currently-connected modules with NULL.

      You should always use a thread-safe wait function in the status check loop, and also give it some sort of exit after a certain amount of elapsed time. This prevents the loop from hoarding resources and also from freezing up the thread.

    3. Dynamic Object Storage. The final "ready to use" capsule class stores encapsulated objects dynamically, allowing the underlying type to be changed arbitrarily. This class is dynamic_capsule. Initially an object of this type is without an encapsulated object, and is therefore useless to modules that wish to access it.

      There are two basic ways to fill a dynamic_capsule with an object. The first is to copy an object compatible with the capsule's interface type. The second is to copy another capsule's contents.

      Because of the potential for ambiguity, this type of capsule cannot be initialized with an object. This is because a potentially limitless number of types can be derived from the interface type and encapsulated in a dynamic_capsule. In order to copy an external object into the capsule, you need to use the auto_capture(Derivative) member function. This will dynamically create a copy of the provided object and store it in the dynamic_capsule:

      #include <ta0kira/global-sentry.hpp>

      class Abstract {};

      class Implementation : public Abstract {};

      int main()
      {
        protect::dynamic_capsule <Abstract> dynamic;
        dynamic.auto_capture( Implementation() );
      }

      Copying another capsule has a much simpler pattern: Just assign a pointer to the other capsule to the dynamic_capsule. Additionally, a pointer to another capsule can be given as a constructor argument.

      #include <ta0kira/global-sentry.hpp>

      class Abstract {};

      class Implementation : public Abstract {};

      int main()
      {
        protect::dynamic_capsule <Abstract> dynamic;
        protect::derivative_capsule <Abstract, Implementation> derivative;
        dynamic = &derivative;
        protect::dynamic_capsule <Abstract> dynamic2(&derivative);
      }

      NOTE: Although the pointer-assignment semantics of copying another capsule implies storage of that pointer, it does not result in the mere storage of that pointer and by no means makes the dynamic_capsule the owner of that pointer. The reasoning behind copying a pointer rather than a reference is that generally the capsule to be copied will be received in pointer form, and rather than forcing the library user to check for NULL and dereference, that is handled internally by the class.

      To purge the dynamic_capsule of its contents, call the purge_contents member function. This destructs the stored object and replaces it with NULL.

      When capturing an external object, copying another capsule, or purging the dynamic_capsule, the status of reading and writing of the current encapsulated object is checked. If any module is viewing or modifying the capsule at the time of the call, the erasure/overwriting is aborted.

      This type of capsule also supports fixed-size arrays. The type of the capsule should be a Type *const so that it can encapsulate arrays of varying sizes. If the capsule type is a fixed-size array then the only type it can enclose will be that size of array of that type. Arrays encapsulated must have the same element type as the interface's elements.

      You should also consider a "condemn and wait" loop with this type of capsule. Please see the static storage section for more details.

    4. Integrated Object Storage. The final method of encapsulation provided by this library is to integrate it with a class to be defined by the library user. This method uses the selfcontained_capsule class, which is used as a base class at the point of implemention of an abstract interface.

      When derived from selfcontained_capsule, a created object will retain the public interface of the derived class in the scope of its creation, allowing for simple initialization of the object by the owning program unit. When made available to other units as a pointer to capsule, however, it shares the same access restrictions and copyability as any other capsule sharing the same interface type.

      This class uses the abstract class to be implemented as the first template argument and the name of the class being derived as the second template argument. The first template argument (the abstract class) becomes the interface type of the capsule. The second argument is provided so that selfcontained_capsule can automatically handle capsule duplication.

      selfcontained_capsule automatically inherits the specified abstract class virtually and privately (the reasoning behind private inheritance is discussed later.) This ensures that the interface type isn't the same as the underlying type, which would create a hole in the encapsulation by allowing anything to static_cast a capsule back to that type, circumventing the protection it's intended to provide. Because of the virtual inheritance, any arguments required during construction of the abstract interface can be provided directly by the class being derived if that class inherits the interface virtually again. The abstract class still needs to have a default constructor, however, so that selfcontained_capsule can safely inherit from it.

      #include <ta0kira/global-sentry.hpp>

      class Abstract
      {
          virtual void function() = 0;
      };

      class Implementation :
          public protect::selfcontained_capsule <Abstract, Implementation>
      {
      private:
          void function() {}
      };

      Deriving a new class from selfcontained_capsule should generally be reserved until implementing a final version of an abstract interface, however a class inheriting selfcontained_capsule may still be safely derived further. This is done at little extra cost by adding the previous selfcontained_capsule-derived class as a third template argument rather than directly inheriting it. As with the abstract interface, selfcontained_capsule will inherit this class virtually.

      #include <ta0kira/global-sentry.hpp>

      struct Abstract
      {
          Abstract() {}
          Abstract(int) {}
          virtual void function() = 0;
      };

      struct Implementation :
          public protect::selfcontained_capsule <Abstract, Implementation>
      {
      private:
          void function() {}
      };

      struct Implementation2 :
          public protect::selfcontained_capsule <Abstract, Implementation2, Implementation>,
          virtual private Abstract
      {
          Implementation2(int value) : Abstract(value) {}
      };

      selfcontained_capsule provides derived classes with a few access status/control functions. These functions are provided so that a derived object can see if a module is currently modifying it, and also to lock out access if it is modifying itself.

      • bool mutex_status() const. This returns true if a module has the capsule opened for writing.

      • bool set_mutex(bool). This sets/unsets the mutex flag to lock out all modules from writing.

      • bool viewing_status() const. This returns true if any module has the capsule opened for reading or writing.

      • void condemn(). This condemns the capsule for destruction, not allowing any access to it after the point of the function call. You should call this in the destructor if your class might take a non-trivial amount of time to destruct.


      Because capsule sets the mutex flag before allowing any module to access its contents for writing, that flag will be set for any subsequent function calls to the object from the module. For that reason your derived class should not check mutex_status when executing a function contained in the abstract interface.

      Because you should avoid a mutex check in the implementations of the abstract functions, you should provide a separate interface to be used locally which does check mutex_status before allowing modification to the object. This secondary interface should be provided to allow object administration by the program unit owning the selfcontained_capsule, while leaving the abstract interface entirely to the outside units. To make this the default behavior, the abstract class is a private base class of selfcontained_capsule, but can be overridden by inheriting that class virtually and publicly again in the class being created. Private inheritance also ensures that the only object or function that can turn a derived object into a pointer to the abstract class is the object itself. NOTE: When allowing the default private inheritance, you will need to declare the implemented abstract functions as private also to prevent public access to them.

      As with all other capsules, you should consider using a "condemn and wait" loop when your capsule goes out of scope (built in to the destructor.) Please see the static storage section regarding how to implement this wait loop.



  3. Capsule Access Patterns.

    The access restrictions imposed by capsules allow only properly-defined modules to access encapsulated objects. This prevents all other functions and objects from accessing those objects, forcing modularization of processes involving its use. Define modules as instructed in this section to gain access to encapsulated objects.

    1. Write Access. All modules requiring write access to encapsulated data must be derived from the capsule_modifier class. This class contains two access entry points: One const and one non-const.

      When accessing a capsule's contents, a pointer to the capsule_modifier is provided to the capsule and it in turn provides the appropriate module entry point with a pseudo-pointer to the encapsulated object. When implementing the entry points for the module, decide whether or not the module itself will be modified as a result. The two entry points are provided as follows:

      • entry_result access_entry(write_object). Use this entry point when the module needs to be modified. This function calls access_entry [const] (below) by default.

      • entry_result access_entry(write_object) const. Use this entry point when the module does not need to be modified. This function returns protect::entry_denied by default.


      write_object is compatible with a pointer to the interface type of the capsule. When creating a module, you should only implement one of the above functions. If you implement the const function then all access operations will call that function. If you implement the non-const function then attempted access by a const module will return protect::entry_denied. When implementing the entry points, always check the pointer for NULL before each use. This is because capsule doesn't screen out NULL pointers so that the module is notified of attempted access. Also, all pointers will turn to NULL if a capsule becomes condemned for destruction. This is to force all modules to exit before a capsule is destructed. You should make a short-term copy of the pointer for each operation to prevent the pointer from turning to NULL between the check and the operation.

      To access the capsule's contents with a capsule_modifier, use the access_contents member function:

      #include <ta0kira/global-sentry.hpp>

      struct Abstract
      {
          virtual bool function() = 0;
      };

      struct Implementation : public Abstract
      {
          bool function() { return true; }
      };

      class ModifyModule :
          public protect::capsule_modifier <Abstract>
      {
          entry_result access_entry(write_object oObj) const
          {
          if (!oObj) return protect::entry_denied;

          Abstract *object = NULL;

          if (!(object = oObj))   return protect::exit_forced;
          if (object->function()) return protect::entry_success;
          else                    return protect::entry_fail;
          }
      };

      int main()
      {
        protect::derivative_capsule <Abstract, Implementation> derivative;
        ModifyModule modifier;
        bool result = !derivative.access_contents(&modifier);
      }

      In the call to access_contents above, modifier is provided to the capsule and the capsule subsequently provides modifier with a pointer to its encapsulated object via the access entry point access_entry.

    2. Read Access. To create a module requiring only read access to a capsule, derive it from the capsule_viewer class. This class has essentially the same patterns as write access except for the entry point names and const access to encapsulated objects.

      The entry points for capsule_viewer are as follows:

      • entry_result view_entry(read_object). Use this entry point when the module needs to be modified. This function calls view_entry [const] (below) by default.

      • entry_result view_entry(read_object) const. Use this entry point when the module does not need to be modified. This function returns protect::entry_denied by default.


      read_object is compatible with a pointer-to-const to the interface type of the capsule. As you may notice, these entry points are essentially identical to those in capsule_modifier, notwithstanding the names and argument type. Reference the write access section for safe implementation guidelines.

      Access for reading follows the same pattern as write access using the view_contents member function:

      #include <ta0kira/global-sentry.hpp>

      struct Abstract
      {
          virtual bool function() const = 0;
      };

      struct Implementation : public Abstract
      {
          bool function() const { return true; }
      };

      class ViewModule :
          public protect::capsule_viewer <Abstract>
      {
          entry_result view_entry(read_object oObj) const
          {
          if (!oObj) return protect::entry_denied;

          const Abstract *object = NULL;

          if (!(object = oObj))   return protect::exit_forced;
          if (object->function()) return protect::entry_success;
          else                    return protect::entry_fail;
          }
      };

      int main()
      {
        protect::derivative_capsule <Abstract, Implementation> derivative;
        ViewModule viewer;
        bool result = !derivative.view_contents(&viewer);
      }

      As with write access, the module is provided to the capsule which then provides the module with a pointer to the encapsulated object via the view_entry entry point.

    3. Access Results. The following returns are used internally, and should be used by all modules, in capsule-accessing functions:

      • const int protect::entry_success. Use to denote a successful access operation.

      • const int protect::entry_denied. Used to denote that partial entry was made but the operation was denied. This should happen if a capsule is condemned or if the pointer provided is initially NULL.

      • const int protect::entry_fail. Use when entry is made but the operation failed.

      • const int protect::entry_wait. This is returned when a capsule is being modified and write access has been requested.

      • const int protect::exit_forced. Use when a pointer turns to NULL during an access operation.

      • const int protect::pre_entry_error. This is used when a wrapper access function aborts attempted access to a capsule.




  4. Independent Capsule Duplication.

    The primary method of duplicating a capsule is to assign it to a dynamic_capsule. This is ideal in most cases because it will automatically delete the copy when it goes out of scope, but that behavior isn't always desired. When a capsule needs to be copied independently of scope, use the guidelines in this section.

    1. Dynamically-allocated Duplicates. To create a copy of a capsule and its contents using dynamic memory, call the clone member function. This will create a full copy of the capsule to include what it contains and must be deleted by the program at some point.

      #include <ta0kira/global-sentry.hpp>

      int main()
      {
        protect::literal_capsule <int> integer;
        protect::capsule <int> *integerCopy = integer.clone();
        delete integerCopy;
      }


    2. Pre-allocated Duplicates. To copy a capsule to a location which already contains allocated memory, call the clone_to function providing the memory location as the only argument. This is equivalent to in-place new; therefore, the copy should not deleted. You should call the destructor when finished, however. To ensure that enough memory is available, call the clone_size function first.

      #include <ta0kira/global-sentry.hpp>

      int main()
      {
        protect::literal_capsule <int> integer;
        unsigned char rawMemory[1024];
        protect::capsule <int> *integerCopy = (protect::capsule <int>*) rawMemory;

        if (integer.clone_size() <= 1024)
        {
        integer.clone_to(integerCopy);
        integerCopy->protect::capsule <int> ::~capsule <int> ();
        }
      }




  5. Unix-specific Access Functionality.

    The GlobalSentry library provides a set of functions to Unix systems which allow automatic capsule access control timing. This is intended for multi-threaded applications, but does not require threads to work.

    Include the header <ta0kira/global-sentry-unix.hpp> instead of <ta0kira/global-sentry.hpp> throughout the entire program to use Unix functionality. You must compile and link to <ta0kira/global-sentry-unix.cpp> in the final program or library (this may be included in main.cpp as an alternative.)

    1. Automatic Access Functions. These functions allow retry attempts and a time limit for the specified operations:

      • void protect::friendly_capsule_destruct(capsule*). This function condemns the given capsule and waits a limited amount of time for all modules to exit. Use this in derivatives of selfcontained_capsule in the destructor. literal_capsule and derivative_capsule implement this automatically in their destructors if the Unix header is used.

      • capsule::entry_result protect::capsule_auto_access(capsule*, capsule_modifier* / const capsule_modifier*). Use this for controlled access to a capsule for modification. This retries access for as long as the capsule is locked, up to a preset time limit. The retry rate is accelerated with time to give capsules waiting longer a better chance of access. This acceleration rate is decreased over time so that eventually there is no more acceleration, preventing a wait operation from taking up too much processor time.

      • bool protect::capsule_rattle(capsule*). This "rattles" the capsule to force all modules to exit. This is similar to friendly_capsule_destruct except this is a temporary measure to free a capsule. This is done by temporarily condemning the capsule then reviving it after a certain elapsed time. If the capsule cannot be revived then false is returned.


    2. Access Function Parameters. These parameters control the behavior of the functions in the previous section. All timing is ideal, using calculated time instead of measured time, however I'm considering time measurement for future versions to compensate for differences in requested and actual sleep time as well as offsets for processing time. For this version, please consider all time to be measured in "courtesy interval" time; not to include time taken in addition to that by the operating system and processor.

      • struct timespec protect::check_retry_time. This is the time specification for the interval between checks in the friendly_capsule_destruct loop. The default is 0.025s.

      • struct timespec protect::max_check_retry_time. This is the target maximum elapsed time a friendly_capsule_destruct loop will process before it exits. The default is 0.5s.

      • struct timespec protect::access_retry_time. This is the initial interval between retries when using capsule_auto_access. The default is 0.25s.

      • struct timespec protect::max_access_retry_time. This is the target maximum elapsed time a capsule_auto_access loop will process before it exits. The default is 90.0s.

      • struct timespec protect::rattle_time. This is the amount of time a capsule_rattle operation will wait before reviving the capsule and returning. The default is 0.25s.

      • double protect::access_acceleration. This is the amount of retry acceleration used in a capsule_auto_access operation. Calling this rate a, initially it changes the retry interval at a rate of 1/(1 + a) per second. The default is 0.05, or +5%/s acceleration in retry rate.

      • double protect::access_acceleration_control. This is the rate of reduction of the acceleration rate for a capsule_auto_access operation. This reduces the acceleration rate over time to eventually eliminate acceleration. Calling this rate c, it changes the acceleration rate at a rate of 1 - c per second. The default is 0.025, or -2.5%/s^2 deceleration in retry acceleration.


      Use the following functions to approximate the outcome of parameter tuning:

      • double protect::cycle_time_at(double). Provide an elapsed time in seconds to find out the interval time between retries for a capsule_auto_access call. The returned result will be the approximate delay between attemts to access the capsule after the function has waited for the given amount of time.

      • double protect::cycle_time_max(). This is equivalent to cycle_time_at for max_access_retry_time.

      • unsigned int protect::total_retry_at(double). Provide an elapsed time in seconds to find the number of retries possible at that point in a capsule_auto_access call. The returned result will be the approximate number of retries the function will make up to that point if no access is given.

      • unsigned int protect::total_retry_max(). This is equivalent to total_retry_at for max_access_retry_time.





  6. Skepticism and Notes.

    Skepticism regarding some apparently strange features and notes impertinent to being able to use this library safely and properly.

    1. Immediate Access to Objects. Why don't all of the implemented capsule classes allow write access to the encapsulated object after initialization? The main reason for this is mutex control. If even the program unit owning the capsule can access its contents openly, there is no way to ensure that it doesn't modify something while something else is trying to do the same concurrently. Although selfcontained_capsule apparently allows this, instructions are provided for how to safely access the capsules locally. Please see that section for more information.

    2. Pointer-copying Capsule? Why isn't there a capsule class that mirrors a pointer to an existing object? This library's central principle is to allow object storage for the purposes of controlling access. Transferring objects to other program units is a useful result of encapsulating the objects, but encapsulating them is the primary purpose. In other words, this library facilitates the protection of data by means of storing and controlling it, and provides assurance of pointer validity by doing the same. As a final point, a stored pointer doesn't allow the capsule to know the underlying type, making it impossible to uphold the precedent of full capsule copyability.

    3. dynamic_capsule Cloning. When assigning a capsule to a dynamic_capsule, the dynamic_capsule calls the clone function on the capsule to be copied. It takes possession of the copy and deletes the copy when it destructs. When capturing an object, it creates a derivative_capsule and does the same.

      When calling a clone function on a dynamic_capsule, rather than creating a new dynamic_capsule it calls the corresponding function on the capsule which it contains. This prevents unnecessary embedded dynamic memory ownership. If a dynamic_capsule is empty then it cannot be duplicated.

    4. Change of Purpose. This project started as an experiment in abstraction several years ago and has since undergone many many changes to make it something that is actually useful. For some time, until the 2.0 release, I'd thought that the best use of this library was to protect "sent" objects rather than to protect "retrieved" objects. The primary difference here is providing objects to functions vs. allowing functions to access objects.

      While on a quest to create a truly useful example of the various possible implementations, I found that this library provides next to nothing when used to protect objects used as function arguments. Something that's a procedure is still a procedure even when using modules. When considering independent units using centralized information, however, this library becomes much more useful. The former provides protection in the direction of declining access facets, whereas the latter provides protection in the opposite direction; that of increasing access facets: Obviously in greater need of protection. For these reasons I've decided to focus on centralized object protection, however I haven't yet discounted the library's potential for allowing "floating objects" within a program that are really only useful to an independent library.

    5. Change of Format. After years of refusing to change the style of my code, I finally decided to adopt the Standard Template Library naming convention for this library. In the end I think this will make the library less intimidating to new users and marks its next major step toward main stream use. Additionally, it symbolizes a change from a personal project to a contribution to the world of C++. I've chosen the same convention as the STL for two reasons. The first is that many other libraries use this convention, and the second is that the naming style should be new to no one who is involved enough in C++ to download this library.

    6. Change of Name. For version 3.0, I've changed the name of this library from the Capsule library to the GlobalSentry library. I find this to be a more suitable name due to the trimming down of purpose over the previous few releases. Additionally, I believe the name "Capsule" is entirely too generic and at this point does the library a great injustice as far as community visibility. The actual object names haven't changed because they are as they need to be at this point.



This tutorial provides a basic overview of the GlobalSentry library, but no real applying guidance. Rather than try to write out how to apply these instructions through more instructions, I find it best to provide a good example. Please see the examples directory for more information on how to apply this library to your programs. If you'd like additional support, please contact me at:

ta0kira@users.sourceforge.net


Kevin P. Barry


Download
SourceForge.net Logo