Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component bdlat_choicefunctions
[Package bdlat]

Provide a namespace defining choice functions. More...

Namespaces

namespace  bdlat_ChoiceFunctions

Detailed Description

Outline
Purpose:
Provide a namespace defining choice functions.
Classes:
bdlat_ChoiceFunctions namespace for calling choice functions
See also:
Component bdlat_selectioninfo
Description:
The bdlat_ChoiceFunctions namespace provided in this component defines parameterized functions that expose "choice" behavior for "choice" types. See the bdlat package-level documentation for a full description of "choice" types.
The functions in this namespace allow users to:
  • make a selection using either a selection id or a selection name (makeSelection),
  • manipulate the current selection using a parameterized manipulator (manipulateSelection),
  • access the current selection using a parameterized accessor (accessSelection), and
  • obtain the id for the current selection (selectionId).
Also, the meta-function IsChoice contains a compile-time constant VALUE that is non-zero if the parameterized TYPE exposes "choice" behavior through the bdlat_ChoiceFunctions namespace.
This component specializes all of these functions for types that have the bdlat_TypeTraitBasicChoice trait.
Types that do not have the bdlat_TypeTraitBasicChoice trait can be plugged into the bdlat framework. This is done by overloading the bdlat_choice* functions inside the namespace of the plugged in type. For example, suppose there is a type called mine::MyChoice. In order to plug this type into the bdlat framework as a "Choice", the following functions must be declared and implemented in the mine namespace:
  namespace mine {

  // MANIPULATORS
  int bdlat_choiceMakeSelection(MyChoice *object, int selectionId);
      // Set the value of the specified 'object' to be the default for
      // the selection indicated by the specified 'selectionId'.  Return
      // 0 on success, and non-zero value otherwise (i.e., the selection
      // is not found).

  int bdlat_choiceMakeSelection(MyChoice  *object,
                                const char *selectionName,
                                int         selectionNameLength);
      // Set the value of the specified 'object' to be the default for
      // the selection indicated by the specified 'selectionName' of the
      // specified 'selectionNameLength'.  Return 0 on success, and
      // non-zero value otherwise (i.e., the selection is not found).

  template <typename MANIPULATOR>
  int bdlat_choiceManipulateSelection(MyChoice *object,
                                      MANIPULATOR& manipulator);
      // Invoke the specified 'manipulator' on the address of the
      // (modifiable) selection of the specified 'object', supplying
      // 'manipulator' with the corresponding selection information
      // structure.  Return -1 if the selection is undefined, and the
      // value returned from the invocation of 'manipulator' otherwise.

  // ACCESSORS
  template <typename ACCESSOR>
  int bdlat_choiceAccessSelection(const MyChoice& object,
                                  ACCESSOR& accessor);
      // Invoke the specified 'accessor' on the (non-modifiable)
      // selection of the specified 'object', supplying 'accessor' with
      // the corresponding selection information structure.  Return -1 if
      // the selection is undefined, and the value returned from the
      // invocation of 'accessor' otherwise.

  int bdlat_choiceSelectionId(const MyChoice& object);
      // Return the id of the current selection if the selection is
      // defined, and 0 otherwise.

  }  // close namespace 'mine'
Also, the IsChoice meta-function must be specialized for the mine::MyChoice type in the bdlat_ChoiceFunctions namespace.
Usage:
Suppose you had a union embedded inside a struct. The struct also contains a d_selectionId member that specifies which member of the union is selected. The default constructor of the struct makes the selection undefined:
  namespace BloombergLP {

  namespace mine {

  struct MyChoice {
      // This struct represents a choice between a 'char' value, an 'int'
      // value, and a 'float' value.

      // CONSTANTS
      enum {
          UNDEFINED_SELECTION_ID = -1,
          CHAR_SELECTION_ID      = 0,
          INT_SELECTION_ID       = 1,
          FLOAT_SELECTION_ID     = 2
      };

      // DATA MEMBERS
      union {
          char  d_charValue;
          int   d_intValue;
          float d_floatValue;
      };
      int d_selectionId;

      // CREATORS
      MyChoice()
      : d_selectionId(UNDEFINED_SELECTION_ID)
      {
      }
  };
We can now make MyChoice expose "choice" behavior by implementing bdlat_ChoiceFunctions for MyChoice. First, we should forward declare all the functions that we will implement inside the mine namespace:
  // MANIPULATORS
  int bdlat_choiceMakeSelection(MyChoice *object, int selectionId);
      // Set the value of the specified 'object' to be the default for the
      // selection indicated by the specified 'selectionId'.  Return 0 on
      // success, and non-zero value otherwise (i.e., the selection is not
      // found).

  int bdlat_choiceMakeSelection(MyChoice   *object,
                                const char *selectionName,
                                int         selectionNameLength);
      // Set the value of the specified 'object' to be the default for the
      // selection indicated by the specified 'selectionName' of the
      // specified 'selectionNameLength'.  Return 0 on success, and non-zero
      // value otherwise (i.e., the selection is not found).

  template <class MANIPULATOR>
  int bdlat_choiceManipulateSelection(MyChoice     *object,
                                      MANIPULATOR&  manipulator);
      // Invoke the specified 'manipulator' on the address of the
      // (modifiable) selection of the specified 'object', supplying
      // 'manipulator' with the corresponding selection information
      // structure.  Return -1 if the selection is undefined, and the value
      // returned from the invocation of 'manipulator' otherwise.

  // ACCESSORS
  template <class ACCESSOR>
  int bdlat_choiceAccessSelection(const MyChoice& object,
                                  ACCESSOR&       accessor);
      // Invoke the specified 'accessor' on the (non-modifiable) selection of
      // the specified 'object', supplying 'accessor' with the corresponding
      // selection information structure.  Return -1 if the selection is
      // undefined, and the value returned from the invocation of 'accessor'
      // otherwise.

  int bdlat_choiceSelectionId(const MyChoice& object);
      // Return the id of the current selection if the selection is defined,
      // and 0 otherwise.

  }  // close namespace mine
Now, we provide the definitions for each of these functions:
  // MANIPULATORS
  int mine::bdlat_choiceMakeSelection(MyChoice *object,
                                      int       selectionId)
  {
      enum { SUCCESS = 0, NOT_FOUND = -1 };

      switch (selectionId) {
        case MyChoice::CHAR_SELECTION_ID: {
          object->d_selectionId = selectionId;
          object->d_charValue   = 0;

          return SUCCESS;                                           // RETURN
        }
        case MyChoice::INT_SELECTION_ID: {
          object->d_selectionId = selectionId;
          object->d_intValue    = 0;

          return SUCCESS;                                           // RETURN
        }
        case MyChoice::FLOAT_SELECTION_ID: {
          object->d_selectionId = selectionId;
          object->d_floatValue  = 0;

          return SUCCESS;                                           // RETURN
        }
        case MyChoice::UNDEFINED_SELECTION_ID: {
          object->d_selectionId = selectionId;

          return SUCCESS;                                           // RETURN
        }
        default: {
          return NOT_FOUND;                                         // RETURN
        }
      }
  }

  int mine::bdlat_choiceMakeSelection(MyChoice   *object,
                                      const char *selectionName,
                                      int         selectionNameLength)
  {
      enum { NOT_FOUND = -1 };

      if (bdlb::String::areEqualCaseless("charValue",
                                        selectionName,
                                        selectionNameLength)) {
          return bdlat_choiceMakeSelection(object,
                                           MyChoice::CHAR_SELECTION_ID);
                                                                    // RETURN
      }

      if (bdlb::String::areEqualCaseless("intValue",
                                        selectionName,
                                        selectionNameLength)) {
          return bdlat_choiceMakeSelection(object,
                                           MyChoice::INT_SELECTION_ID);
                                                                    // RETURN
      }

      if (bdlb::String::areEqualCaseless("floatValue",
                                        selectionName,
                                        selectionNameLength)) {
          return bdlat_choiceMakeSelection(object,
                                           MyChoice::FLOAT_SELECTION_ID);
                                                                    // RETURN
      }

      return NOT_FOUND;
  }
For the manipulateSelection and accessSelection functions, we need to create a temporary bdlat_SelectionInfo object and pass it along when invoking the manipulator or accessor. See the bdlat_selectioninfo component-level documentation for more information. The implementation of the remaining functions are as follows:
  template <class MANIPULATOR>
  int mine::bdlat_choiceManipulateSelection(MyChoice     *object,
                                            MANIPULATOR&  manipulator)
  {
      switch (object->d_selectionId) {
        case MyChoice::CHAR_SELECTION_ID: {
          bdlat_SelectionInfo info;

          info.annotation()     = "Char Selection";
          info.formattingMode() = bdlat_FormattingMode::e_DEFAULT;
          info.id()             = MyChoice::CHAR_SELECTION_ID;
          info.name()           = "charValue";
          info.nameLength()     = 9;

          return manipulator(&object->d_charValue, info);           // RETURN
        }
        case MyChoice::INT_SELECTION_ID: {
          bdlat_SelectionInfo info;

          info.annotation()     = "Int Selection";
          info.formattingMode() = bdlat_FormattingMode::e_DEFAULT;
          info.id()             = MyChoice::INT_SELECTION_ID;
          info.name()           = "intValue";
          info.nameLength()     = 8;

          return manipulator(&object->d_intValue, info);            // RETURN
        }
        case MyChoice::FLOAT_SELECTION_ID: {
          bdlat_SelectionInfo info;

          info.annotation()     = "Float Selection";
          info.formattingMode() = bdlat_FormattingMode::e_DEFAULT;
          info.id()             = MyChoice::FLOAT_SELECTION_ID;
          info.name()           = "floatValue";
          info.nameLength()     = 10;

          return manipulator(&object->d_floatValue, info);          // RETURN
        }
        default:
          BSLS_ASSERT_SAFE(!"Invalid selection!");
      }
      return 0;
  }

  // ACCESSORS

  template <class ACCESSOR>
  int mine::bdlat_choiceAccessSelection(const MyChoice& object,
                                        ACCESSOR&       accessor)
  {
      switch (object.d_selectionId) {
        case MyChoice::CHAR_SELECTION_ID: {
          bdlat_SelectionInfo info;

          info.annotation()     = "Char Selection";
          info.formattingMode() = bdlat_FormattingMode::e_DEFAULT;
          info.id()             = MyChoice::CHAR_SELECTION_ID;
          info.name()           = "charValue";
          info.nameLength()     = 9;

          return accessor(object.d_charValue, info);                // RETURN
        }
        case MyChoice::INT_SELECTION_ID: {
          bdlat_SelectionInfo info;

          info.annotation()     = "Int Selection";
          info.formattingMode() = bdlat_FormattingMode::e_DEFAULT;
          info.id()             = MyChoice::INT_SELECTION_ID;
          info.name()           = "intValue";
          info.nameLength()     = 8;

          return accessor(object.d_intValue, info);                 // RETURN
        }
        case MyChoice::FLOAT_SELECTION_ID: {
          bdlat_SelectionInfo info;

          info.annotation()     = "Float Selection";
          info.formattingMode() = bdlat_FormattingMode::e_DEFAULT;
          info.id()             = MyChoice::FLOAT_SELECTION_ID;
          info.name()           = "floatValue";
          info.nameLength()     = 10;

          return accessor(object.d_floatValue, info);               // RETURN
        }
        default:
          BSLS_ASSERT_SAFE(!"Invalid selection!");
      }
      return 0;
  }

  int mine::bdlat_choiceSelectionId(const MyChoice& object)
  {
      return object.d_selectionId;
  }
Finally, we need to specialize the IsChoice meta-function in the bdlat_ChoiceFunctions namespace for the mine::MyChoice type. This makes the bdlat infrastructure recognize mine::MyChoice as a choice abstraction:
  namespace bdlat_ChoiceFunctions {

  template <>
  struct IsChoice<mine::MyChoice> {
      enum { VALUE = 1 };
  };

  }  // close namespace bdlat_ChoiceFunctions
  }  // close enterprise namespace
The bdlat infrastructure (and any component that uses this infrastructure) will now recognize MyChoice as a "choice" type. For example, suppose we have the following XML data:
  <?xml version='1.0' encoding='UTF-8' ?>
  <MyChoice>
      <intValue>321</intValue>
  </MyChoice>
Using the balxml_decoder component, we can load this XML data into a MyChoice object:
  void usageExample(bsl::istream& inputData)
  {
    using namespace BloombergLP;

    MyChoice object;

    assert(MyChoice::UNDEFINED_SELECTION_ID == object.d_selectionId);

    balxml::DecoderOptions options;
    balxml::MiniReader     reader;
    balxml::ErrorInfo      errInfo;

    balxml::Decoder decoder(&options, &reader, &errInfo);
    int result = decoder.decode(inputData, &object);

    assert(0                           == result);
    assert(MyChoice::INT_SELECTION_ID  == object.d_selectionId);
    assert(321                         == object.d_intValue);
  }
Note that the bdlat framework can be used for functionality other than encoding/decoding into XML. When mine::MyChoice is plugged into the framework, then it will be automatically usable within the framework. For example, the following snippets of code will print out the selection value of a choice object:
  struct PrintSelection {
      // Print each visited object to the bound 'd_stream_p' object.

      // DATA MEMBERS
      bsl::ostream *d_stream_p;

      template <class TYPE, class INFO>
      int operator()(const TYPE& object, const INFO& info)
      {
        (*d_stream_p) << info.name() << ": " << object << bsl::endl;
        return 0;                                                   // RETURN
      }
  };

  template <class TYPE>
  void printChoiceSelection(bsl::ostream& stream, const TYPE& object)
  {
      using namespace BloombergLP;

      PrintSelection accessor;
      accessor.d_stream_p = &stream;

      bdlat_choiceAccessSelection(object, accessor);
  }
Now we have a generic function that takes an output stream and a choice object, and prints out each choice seletion with its name and value. We can use this generic function as follows:
  void printMyChoice(bsl::ostream& stream)
  {
      using namespace BloombergLP;

      mine::MyChoice object;

      object.d_selectionId = mine::MyChoice::INT_SELECTION_ID;
      object.d_intValue    = 321;

      printChoiceSelection(stream, object);
  }
The function above will print the following to provided stream:
  intValue: 321