### Navigation

- index
- next |
- previous |
- OME Files 0.2.2 documentation »

# C++ conversion details

The C++ codebase has been primarily a conversion of the original Java codebase, with some additional helper functions and classes added where needed. The intention is that the basic interfaces and classes should be identical between the two languages unless this is prevented by fundamental differences between the languages.

This section is intended to be useful for

- Users of the existing Java interface, who wish to understand the differences between the two implementations
- Developers who wish to work on the C++ interface

In addition to documenting the specific language and class compatibility issues, this section also documents the idioms in use in the C++ code which might not be immediately clear by looking at the API reference, and which may not be familiar to Java developers.

## C++ and Java type incompatibility

While C++ and Java have some basic syntactical similarities, there are several basic differences in their type systems.

### Java types

Java has primitive types and classes.

    int i;
    double d;

- No unsigned primitive integer types

    Pixels pixels = new Pixels();

- All classes are derived from root `Object`
- Objects are by reference only
- Objects and arrays are always allocated with `new`
- Destruction is non-deterministic
- All passing is by value (primitives and object references)

    Pixels[] array = new Pixels[5];

- Arrays have an intrinsic size.
- Arrays are safe to index out of bounds (an exception is thrown).

### C++ types

C++ has primitive types, structures and classes.

    int16_t i1;
    uint32_t i2;
    double d;

- Primitive integer types may be signed or unsigned.
- Integer types are of defined size.

    // Allocate on the stack, or as a struct or class member:
    Pixels         pixels;

    // Allocate on the heap
    Pixels        *pixelsptr1 = new Pixels();

    // Pointer to existing instance
    const Pixels  *pixelsptr2 = &pixels;

    // Reference to existing instance
    Pixels&        pixelsref(pixels);

- Classes have no common root
- All types may be instances, pointers or references
- Object construction may be on the stack, on the heap using `new` or in place using placement `new`.
- Pointers and references may refer to `const` type
- Pointers may be `const`
- References are implicitly `const` (similar to `final`)
- Destruction is deterministic
- `new` should never be used in modern C++ code (see below)

    Pixels array[5];

- Arrays “decay” to bare pointers
- Arrays are not safe to index out of bounds
- Size information lost at runtime
- Never use arrays outside static initializers

### Simplified type names

`typedef` is used to create an alias for an existing type.

    typedef std::vector<std::string> string_list;
    string_list l;
    string_list::const_iterator i = l.begin();
    // NOT std::vector<std::string>::const_iterator

    typedef std::vector<Pixels> plist;
    plist pl(6);
    plist::size_type idx = 2;
    // size_type NOT unsigned int or uint32_t
    pl.at(idx) = ...;

Used in standard container types e.g. `size_type`, `value_type` and in classes and class templates. Consistency is needed for generic programming—use the standard type names to enable interoperability with standard algorithms.

## Exception handling

### Java

`throws` details which exceptions are thrown by a method. Java exceptions are also “checked”, requiring the caller to catch and handle all exceptions which might be thrown, aside from `RuntimeException` and its subclasses.

### C++

C++ has exception specifications like Java, however they are useless aside from `nothrow`. This is because if an exception is thrown which does not match the specification, it will abort the program with a `bad_exception` which makes them unusable in practice.

Exceptions can be thrown at any point with the exception that they should never be thrown in a destructor. It is not necessary or typical to check exceptions except where needed. All code must be exception-safe given that an exception could be thrown at any point; the design considerations for exception safety are covered below.

## Interfaces

Java supports single-inheritance, plus interfaces. C++ supports true multiple-inheritance, which is rather more flexible, at the expense of being rather more complicated and dangerous. However, the Java single-inheritance-plus-interfaces model can be implemented in C++ using a subset of the facilities provided by multiple inheritance. Rather than being enforced by the language, it is a set of idioms. These must be rigorously followed or else things will fail horribly!

C++ interfaces are classes with:

- No instance variables
- Pure virtual methods
- protected` default constructor
- `public virtual` destructor
- Deleted copy constructor and assignment operator

C++ classes implementing interfaces:

- Use `public` inheritance for parent class
- Use `virtual public` inheritance for implemented interfaces
- Have a `virtual` destructor

When compiled with optimization enabled, the interface classes should have zero storage overhead. If implementing classes do not use `virtual public` inheritance, compilation will fail as soon as a second class in the inheritance hierarchy also implements the interface.

## Reference handling and memory management

### Pointer problems

Plain (or “dumb”) C++ pointers can be dangerous if used incorrectly. The OME-Files API make a point of never using them unless absolutely necessary. For automatic objects allocated on the stack, allocation and deallocation is automatic and safe:

    {
      Image i(filename);
      i.read_plane();

      // Object destroyed when i goes out of scope
    }

In this case, the object’s destructor was run and the memory freed automatically.

Looking at the case where a pointer is used to reference manually-allocated memory on the heap:

    {
      Image *i = new Image(filename);

      i->read_plane();

      // Memory not freed when pointer i goes out of scope
    }

In this case `new` was not paired with the corresponding `delete`, resulting in a memory leak. This is the code with the “leak” fixed:

      {
      Image *i = new Image(filename);

      i->read_plane(); // throws exception; memory leaked

      delete i; // never called
    }

`new` and `delete` are now paired, but the code is not exception-safe. If an exception is thrown, memory will still be leaked. Manual memory management requires correct clean up for every exit point in the function, including both all `return` statements and thrown exceptions. Here, we handle this correctly:

    {
      Image *i = new Image(filename);

      try {
        i->read_plane(); // throws exception
      } catch (const std::runtime_error& e) {
        delete i; // clean up
        throw; // rethrow
      }

      delete i; // never called for exceptions
    }

However, this does not scale. This is painful and error prone when scaled to an entire codebase. Even within this simple function, there is only a single variable with a single exception and single return to deal with. Imagine the combinatorial explosion when there are several variables with different lifetimes and scopes, multiple return points and several exceptions to handle–this is easy to get wrong, so a more robust approach is needed.

Use of `new` is not in the general case safe or sensible. The OME-Files API never passes pointers allocated with `new`, nor requires any manual memory management. Instead, “smart” pointers are used throughout to manage memory safely and automatically.

### `ome::compat::shared_ptr` as a “smart” pointer

The unsafe example above, has been rewritten to use `ome::compat::shared_ptr`:

    // Start of block
    {
      ome::compat::shared_ptr<Image> i(ome::compat::make_shared<Image>(filename));

      i->read_plane(); // throws exception

      // Memory freed when i's destructor is
      // run at exit of block scope
    }

Rather than managing the memory by hand, responsibility for this is delegated to a “smart” pointer, `ome::compat::shared_ptr`. The memory is freed by the `ome::compat::shared_ptr` destructor which is run at the end of the block scope, on explicit `return`, or when cleaned up by exception stack unwinding.

Note

`ome::compat::shared_ptr` is either a `std::shared_ptr` or a `boost::shared_ptr`, depending upon whether C++11 features are avaiable or not, respectively.

- `shared_ptr` object lifetime manages the resource
- `new` replaced with `ome::compat::make_shared`
- May be used as class members; lifetime is tied to class instance
- Clean up for all exit points is automatic and safe
- Allows ownership transfer and sharing
- Allows reference without ownership using `weak_ptr`
- `weak_ptr` references the object but does not prevent it being freed when the last `shared_ptr` reference is lost; this is useful for cycle breaking and is used by the OME XML model objects for references

### Resource Acquisition Is Initialization

Resource Acquisition Is Initialization (RAII) is a programming idiom used throughout modern C++ libraries and applications, including the Standard Library,

- A class is a proxy for a resource
- The resource is acquired when object is initialised
- The resource is released when object is destroyed
- Any resource may be managed (e.g. memory, files, locks, mutexes)
- The C++ language and runtime guarantees make resource management deterministic and reliable
- Safe for use in any scope
- Exception safe
- Used throughout modern C++ libraries and applications

Because this relies implicitly upon the deterministic object destruction guarantees made by the C++ language, this is not used widely in Java APIs which often require manual management of resources such as open files. Used carefully, RAII will prevent resource leaks and result in robust, safe code.

The `FormatReader` API is currently not using RAII due to the use of the `FormatHandler::setId()` interface.

### C++ reference variants

     //              Non-constant                                 Constant
     // -------------------------------------  ----------------------------------------------
     // Pointer
                                    Image *i;                                 const Image *i;
                             Image * const i;                          const Image * const i;

     // Reference
                                    Image& i;                                 const Image& i;

     // Shared pointer
            ome::compat::shared_ptr<Image> i;         ome::compat::shared_ptr<const Image> i;
      const ome::compat::shared_ptr<Image> i;   const ome::compat::shared_ptr<const Image> i;

     // Shared pointer reference
           ome::compat::shared_ptr<Image>& i;        ome::compat::shared_ptr<const Image>& i;
     const ome::compat::shared_ptr<Image>& i;  const ome::compat::shared_ptr<const Image>& i;

     // Weak pointer
              ome::compat::weak_ptr<Image> i;           ome::compat::weak_ptr<const Image> i;
        const ome::compat::weak_ptr<Image> i;     const ome::compat::weak_ptr<const Image> i;

    // Weak pointer reference
             ome::compat::weak_ptr<Image>& i;          ome::compat::weak_ptr<const Image>& i;
       const ome::compat::weak_ptr<Image>& i;    const ome::compat::weak_ptr<const Image>& i;

Java has one reference type. Here, we have 22. Clearly, not all of these will typically be used. Below, a subset of these are shown for use for particular purposes.

Class member types:

    Image i;                             // Concrete instance
    ome::compat::shared_ptr<Image> i;    // Reference
    ome::compat::weak_ptr<Image> i;      // Weak reference

Wherever possible, a concrete instance should be preferred. This is not possible for polymorphic types, where a reference is required. In this situation, an `ome::compat::shared_ptr` is preferred if the class owns the member and/or needs control over its lifetime. If the class does not have ownership then an `ome::compat::weak_ptr` will allow safe access to the object if it still exists. In circumstances where manual lifetime management is required, e.g. for performance, and the member is guaranteed to exist for the duration of the object’s lifetime, a plain pointer or reference may be used. A pointer will be used if it is possible for it to be `null`, or it may be reassigned more than once, or if is assigned after initial construction. If properly using RAII, using references should be possible and preferred over bare pointers in all cases.

Argument types:

    // Ownership retained
    void read_plane(const Image& image);
    // Ownership shared or transferred
    void read_plane(const ome::compat::shared_ptr<Image>& image);

Passing primitive types by value is acceptable. However, passing a struct or class by value will implicitly copy the object into the callee’s stack frame, which may be expensive (and requires a copy constructor which will not be guaranteed or even possible for polymorphic types). Passing by reference avoids the need for any copying, and passing by `const` reference will prevent the callee from modifying the object, also making it clear that there is no transfer of ownership. Passing using an `ome::compat::shared_ptr` is possible but not recommended—the copy will involve reference counting overhead which can kill multi-threaded performance since it requires synchronization between all threads; use a `const` reference to an `ome::compat::shared_ptr` to avoid the overhead. If ownership should be transferred or shared with the callee, use a non-`const` reference.

To be absolutely clear, plain pointers are never used and are not acceptable for ownership transfer. A plain reference also makes it clear there is no ownership transfer.

Return types:

                              Image get_image(); // Ownership transferred
                             Image& get_image(); // Ownership retained
     ome::compat::shared_ptr<Image> get_image(); // Ownership shared/trans
    ome::compat::shared_ptr<Image>& get_image(); // Ownership shared

If the callee does not retain a copy of the original object, it can’t pass by reference since it can’t guarantee the object remaining in scope after it returns, hence it must create a temporary value and pass by value. If the callee does retain a copy, it has the option of passing by reference. Passing by reference is preferred when possible. Passing by value implies ownership transfer. Passing by reference implies ownership retention. Passing an `ome::compat::shared_ptr` by value or reference implies sharing ownership since the caller can retain a reference; if passing by value ownership may be transferred since this implies the callee is not retaining a reference to it (but this is not guaranteed).

Again, to be absolutely clear, plain pointers are never used and are not acceptable for ownership transfer. A plain reference also makes it clear there is no ownership transfer.

- Safety: References cannot be `null`
- Storing polymorphic types requires use of a `shared_ptr`
- Referencing polymorphic types may require use of a `shared_ptr`
- Safety: To avoid cyclic dependencies, use `weak_ptr`
- Safety: To allow object destruction while maintaining a safe reference, use `weak_ptr`
- `weak_ptr` is not directly usable
- `weak_ptr` is convertible back to `shared_ptr` for use if the object is still in existence
- C++11 move semantics (`&&`) improve the performance of ownership transfer

## Containers

### Safe array passing

C++ arrays are not safe to pass in or out of functions since the size is not known unless passed separately.

    class Image
    {
      // Unsafe; size unknown
      uint8_t[] getLUT();
           void setLUT(uint8_t[]& lut);
    };

C++ arrays “decay” to “bare” pointers, and pointers have no associated size information.

`ome::compat::array` is a safe alternative. This is either a C++11 `std::array` or `boost::array` with older compilers.

    class Image
    {
      typedef ome::compat::array<uint8_t, 256> LUT;

      // Safe; size defined
      const LUT& getLUT() const;
            void setLUT(const LUT&);
    };

`ome::compat::array` is a array-like object (a class which behaves like an array). Its type and size are defined in the template, and it may be passed around like any other object. Its `array::at()` method provides strict bounds checking, while its index `array::operator[]()` provides unchecked access.

## Storing and passing unrelated types

### Types with a common base

    std::vector<ome::compat::shared_ptr<Base> > v;
    v.push_back(ome::compat::make_shared<Derived>());

This can store any type derived from `Base`. An `ome::compat::shared_ptr` is essential. Without it, bare pointers to the base would be stored, and memory would be leaked when elements are removed from the container (unless externally managed [generally unsafe]). The same applies to passing polymorphic types.

Java containers can be problematic:

- Java can store root `Object` in containers
- Java can pass and return root `Object` in methods.
- This is not possible in C++: there is no root object.
- An alternative approach is needed.

### Arbitrary types

`boost::any` may be used to store any type:

    std::vector<boost::any> v;
    v.push_back(Anything);

- Assign and store any type
- Type erasure (similar to Java generics)
- Use for containers of arbitrary types
- Flexible, but need to cast to each type used to extract
- Code will not be able to handle all possible types meaningfully

This is the most flexible solution, but in order to get a value back out, requires casting it to its specific type. This can mean a situation could arise where values are stored of types which cannot be handled since it is not possible to write the code to handle every single possibility ahead of time. However, if the open-ended flexibility is needed, this is available.

### A fixed set of types

`boost::variant` may be used to store a limited set of different types: This avoids the `boost::any` problem of not being able to handle all possible types, since the scope is limited to a set of allowed types, and a `static_visitor` can ensure that all types are supported by the code at compile time.

    typedef boost::variant<int, std::string> variants;
    std::vector<variants> v;
    v.push_back(43);
    v.push_back("ATTO 647N");

- Store a set of discriminated types
- “External polymorphism” via `static_visitor`
- Used to store original metadata
- Used to store nD pixel data of different pixel types

This is not an alternative to a common root object. Instead, this is a discriminated union, which can store one of a defined set of “variant” types. A static visitor pattern may be used to generate code to operate on all of the supported types. The variant type may be used as a class member, passed by value, passed by reference or stored in a container like any other type. Due to the way it is implemented to store values, it does not necessarily need wrapping in an `ome::compat::shared_ptr` since it can behave as a value type (depending upon the context).

Java uses polymorphism to store and pass the root `Object` around. The `boost::variant` and `boost::any` approaches use templates to (internally) create a common base and manage the stored objects. However, the end user does not need to deal with this complexity directly—the use of the types is quite transparent.

### Variant example: MetadataMap

This example demonstrates the use of variants with a simple expansion for two different categories of type (scalars and vectors of scalars).

The `MetadataMap` class stores key-value pairs, where the value can be either a string, Boolean, or several integer and floating point types, or vectors of any of these types. When converting the data to other forms, it is necessary to flatten the vector types to a set of separate key-value pairs with the key having a numbered suffix, one for each element in the vector.

    {
      MetadataMap map;
      MetadataMap flat_map (map.flatten());
    }

A flattened map is created using the following method:

    MetadataMap MetadataMap::flatten() const {
      MetadataMap newmap;

      for (MetadataMap::const_iterator i = oldmap.begin();
           i != oldmap.end(); ++i) {
        MetadataMapFlattenVisitor v(newmap, i->first);
        boost::apply_visitor(v, i->second);
      }

      return newmap;
    }

The `MetadataMapFlattenVisitor` is implemented thusly:

    // Flatten MetadataMap vector values
    struct MetadataMapFlattenVisitor : public boost::static_visitor<> {
      MetadataMap& map; // Map of flattened elements
      const MetadataMap::key_type& key; // Current key

      MetadataMapFlattenVisitor
        (MetadataMap&                 map,
         const MetadataMap::key_type& key):
        map(map), key(key) {}

      // Output a scalar value of arbitrary type.
      template <typename T>
      void operator() (const T& v) const {
        map.set(key, v);
      }

      // Output a vector value of arbitrary type.
      template <typename T>
      void operator() (const std::vector<T>& c) const {
        typename std::vector<T>::size_type idx = 1;
        for (typename std::vector<T>::const_iterator i = c.begin();
             i != c.end(); ++i, ++idx) {
          std::ostringstream os;
          os << key << " #" << idx;
          map.set(os.str(), *i);
        }
      }
    };

The `MetadataMapFlattenVisitor` is derived from `boost::static_visitor`, and its templated operator method is specialized and expanded once for each type supported by the variant type used by the map. In the above example, two separate overloaded operators are provided, one for scalar values which is a simple copy, and one for vector values which splits the elements into separate keys in the new map. The important part is the call to `apply_visitor()`, which takes as arguments the visitor object and the variant to apply it to.

This could be done with a large set of conditionals using `boost::get<T>(value)` for each supported type. The benefit of the `boost::static_visitor` approach is that it ensures that all the types are supported at compile time, and in effect results in the same code. If any types are not supported, the code will fail to compile.

### Variant example: VariantPixelBuffer equality comparison

This example demonstrates the use of variants with a combinatorial expansion of types.

The `VariantPixelBuffer` class can contain `PixelBuffer` classes of various pixel types. Comparing for equality is only performed if the pixel types of the two objects are the same:

    {
      VariantPixelBuffer a, b;
      if (a == b) {
        // Buffers are the same.
      }
    }

This is implemented using an overloaded equality operator:

    bool VariantPixelBuffer::operator ==
        (const VariantPixelBuffer& rhs) const
    {
      return boost::apply_visitor(PBCompareVisitor(),
                                  buffer, rhs.buffer);
    }

As before, this is implemented in terms of a `boost::static_visitor`, but note that this time it is specialized for `bool`, meaning that the return type of `apply_visitor()` will also be `bool`, and the operator methods must also return this type.

    struct PBCompareVisitor : public boost::static_visitor<bool> {
      template <typename T, typename U>
      bool operator() (const T& /* lhs */,
                       const U& /* rhs */) const {
        return false;
      }

      template <typename T>
      bool operator() (const T& lhs,
                       const T& rhs) const {
        return lhs && rhs && (*lhs == *rhs);
      }
    };

Unlike the last example, the operator methods now have two arguments, both of which are variant types, and the `apply_visitor()` call is passed two variant objects in addition to the visitor object. This causes the templates to be expanded for all pairwise combinations of the possible types. When the types are not equal, the first templated operator is called, which always returns false. When the types are equal the second operator is called; this checks both operands are not null and then performs an equality comparison using the buffer contents. Given that all the operators are inline, we would hope that a good compiler would cause all the false cases to be optimized out after expansion.

### Variant example: VariantPixelBuffer SFINAE

This example demonstrates the use of variants with SFINAE.

C++ has a concept known as Substitution Failure Is Not An Error (SFINAE), which refers to it not being an error for a candidate template to fail argument substitution during overload resolution. While this is in and of itself a fairly obscure language detail, it enables overloading of a method not just on type, but different categories of type, for example integer and floating point types, signed and unsigned integer types, simple and complex types, or combinations of all of these. This is particularly useful when writing algorithms to process pixel data.

Use of SFINAE has been made accessible through the creation of `boost::enable_if` (`std::enable_if` in C++11), and type traits (type category checking classes such as `is_integer`). The following code is an example of how one might write a visitor for adapting an algorithm to separate integer, floating point, complex floating point and bitmask cases.

    struct TypeCategoryVisitor : public boost::static_visitor<>
    {
      typedef ::ome::files::PixelProperties< ::ome::xml::model::enums::PixelType::BIT>::std_type bit_type;

      TypeCategoryVisitor()
      {}

      // Integer pixel types
      template <typename T>
      typename boost::enable_if_c<
        boost::is_integral<T>::value, void
        >::type
      operator() (ome::compat::shared_ptr< ::ome::files::PixelBuffer<T> >& buf)
      {
        // Integer algorithm.
      }

      // Floating point pixel types
      template <typename T>
      typename boost::enable_if_c<
        boost::is_floating_point<T>::value, void
        >::type
      operator() (ome::compat::shared_ptr< ::ome::files::PixelBuffer<T> >& buf)
      {
        // Floating point algorithm.
      }

      // Complex floating point pixel types
      template <typename T>
      typename boost::enable_if_c<
        boost::is_complex<T>::value, void
        >::type
      operator() (ome::compat::shared_ptr< ::ome::files::PixelBuffer<T> >& buf)
      {
        // Complex floating point algorithm.
      }

      // BIT/bool pixel type.  Note this is a simple overload since it is
      // a simple type, not a category of different types.
      void
      operator() (ome::compat::shared_ptr< ::ome::files::PixelBuffer<bit_type> >& buf)
      {
        // Boolean algorithm.
      }
    };

This visitor may be used with `apply_visitor()` in a similar manner to the previously demonstrated visitors.

`enable_if` has two parameters, the first being a conditional, the second being the return type (in this example, all the methods return `void`). If the conditional is true, then the type expands to the return type and the template is successfully substituted. If the conditional is false (types do not match), then the substitution fails and the template will not be used. Note that the conditional is itself a type, which can be confusing, since all this logic is driven by conditional template expansion.

Normal templates are specialized for a type. This approach allows specialization for different categories of type. Without this approach it would be necessary to write separate overloads for each individual type (each integer type, each floating point type, each complex type, etc.), even when the logic would be identical for e.g. the different integer types. This approach therefore removes the need for unnecessary code duplication, and the type traits checks make each type category explicit to the reader.

### Navigation

- index
- next |
- previous |
- OME Files 0.2.2 documentation »