// bdlat_nullablevalueutil.h                                          -*-C++-*-
#ifndef INCLUDED_BDLAT_NULLABLEVALUEUTIL
#define INCLUDED_BDLAT_NULLABLEVALUEUTIL

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide utilities for operating on 'bdlat' "nullable value" types.
//
//@CLASSES:
//  bdlat::NullableValueUtil: namespace for utility functions on nullables
//
//@SEE_ALSO: bdlat_nullablevaluefunctions, bdlat_typecategory
//
//@DESCRIPTION: This component provides a utility 'struct',
// 'bdlat::NullableValueUtil', which serves as a namespace for a collection of
// function templates providing derived operations for "nullable value" types.
// See {'bdlat_nullablevaluefunctions'} for the set of requirements of
// "nullable value" types in the 'bdlat' framework.  See {'bdlat_typecategory'}
// for more general information about this framework.
//
///Primitive and Derived Functions of Nullable Values
///--------------------------------------------------
// In order to be "plugged in" to the 'bdlat' framework as a "nullable value",
// a type must meet a set of requirements including providing certain function
// overloads (customization points) and specifying certain type traits, as
// specified by the {'bdlat_nullablevaluefunctions'} component.  We call the
// required function overloads the "primitive" operations of "nullable value"
// types.  This component provides "derived" operations, which are operations
// that are exclusively defined in terms of primitive operations, and as such
// can be used with any "nullable value" type.
//
///Usage
///-----
// In this section we show intended usage of this component.
//
///Example 1: Accessing the Held Value And Its Category
/// - - - - - - - - - - - - - - - - - - - - - - - - - -
// Suppose we would like to define a function that detects whether the value
// held by a nullable value is an array.
//
// First, we need to define an accessor functor per
// {'bdlat_typecategory'|'ACCESSOR' Functors} that will be used to detect
// whether the held value is an array:
//..
//  class MyArrayDetector {
//      // DATA
//      bool d_didVisitArray;
//
//    public:
//      // CREATORS
//      MyArrayDetector()
//      : d_didVisitArray(false)
//      {
//      }
//
//      // MANIPULATORS
//      template <class TYPE>
//      int operator()(const TYPE& object, bdlat_TypeCategory::Array)
//      {
//          d_didVisitArray = true;
//          return 0;
//      }
//
//      template <class TYPE, class OTHER_CATEGORY>
//      int operator()(const TYPE&, OTHER_CATEGORY)
//      {
//          d_didVisitArray = false;
//          return 0;
//      }
//
//      // ACCESSORS
//      bool didVisitArray()
//      {
//          return d_didVisitArray;
//      }
//  };
//..
// Then, we can define a utility 'struct', 'MyNullableValueUtil', that provides
// a function for detecting whether or not the held value of a nullable value
// is an array:
//..
//  struct MyNullableValueUtil {
//
//      // CLASS METHODS
//      template <class TYPE>
//      static int isValueAnArray(bool *isArray, const TYPE& object)
//          // Load the value 'true' to the specified 'isArray' if the value
//          // stored in the specified 'object' has the "array" type category,
//          // and load the value 'false' otherwise.  Return 0 on success,
//          // and a non-zero value otherwise.  If a non-zero value is
//          // returned, the value loaded to 'isArray' is unspecified.  The
//          // behavior is undefined if 'object' contains a null value.
//      {
//          BSLS_ASSERT(bdlat_TypeCategoryFunctions::select(object) ==
//                      bdlat_TypeCategory::e_NULLABLE_VALUE_CATEGORY);
//          BSLS_ASSERT(!bdlat_NullableValueFunctions::isNull(object));
//
//          MyArrayDetector detector;
//          int rc = bdlat::NullableValueUtil::accessValueByCategory(object,
//                                                                   detector);
//          if (0 != rc) {
//              return -1;                                            // RETURN
//          }
//
//          *isArray = detector.didVisitArray();
//          return 0;
//      }
//  };
//..
// Finally, we can use this utility to detect whether nullable values are
// arrays:
//..
//  void example()
//  {
//      bdlb::NullableValue<int> valueA(42);
//
//      bool isArray = false;
//      int rc = MyNullableValueUtil::isValueAnArray(&isArray, valueA);
//
//      assert(0 == rc);
//      assert(! isArray);
//
//      bdlb::NullableValue<bsl::vector<int> > valueB;
//      valueB.makeValue(bsl::vector<int>());
//
//      rc = MyNullableValueUtil::isValueAnArray(&isArray, valueB);
//
//      assert(0 == rc);
//      assert(isArray);
//  }
//..

#include <bdlscm_version.h>

#include <bdlat_nullablevaluefunctions.h>
#include <bdlat_typecategory.h>

#include <bslmf_assert.h>

#include <bsls_assert.h>
#include <bsls_platform.h>

namespace BloombergLP {
namespace bdlat {

                          // ========================
                          // struct NullableValueUtil
                          // ========================

struct NullableValueUtil {
    // This 'struct' provides a namespace for a suite of function templates
    // providing non-primitive operations on "nullable value" types.

  private:
    // PRIVATE TYPES
    template <class ACCESSOR>
    class AccessByCategoryAdapter;
        // This private class provides a function-object type that adapts a
        // (categorized) accessor functor to an uncategorized accessor functor.
        // For the definition of an accessor functor, see
        // {'bdlat_typecategory'|'ACCESSOR' Functors}.  An uncategorized
        // accessor functor is one that does not take a second, 'category',
        // argument, such as a functor that may be passed to
        // 'bdlat_NullableValueFunctions::accessValue', for example.

    template <class MANIPULATOR>
    class ManipulateByCategoryAdapter;
        // This private class provides a function-object type that adapts a
        // (categorized) manipulator functor to an uncategorized manipulator
        // functor.  For the definition of a manipulator functor, see
        // {'bdlat_typecategory'|'MANIPULATOR' Functors}.  An uncategorized
        // manipulator functor is one that does not take a second, 'category',
        // argument, such as a functor that may be passed to
        // 'bdlat_NullableValueFunctions::manipulateValue', for example.

  public:
    // CLASS METHODS
    template <class TYPE, class ACCESSOR>
    static int accessValueByCategory(const TYPE& object, ACCESSOR& accessor);
        // Invoke the specified 'accessor' on the non-modifiable value
        // stored in the specified "nullable" 'object' and on a prvalue of
        // the category tag type for the dynamic category of the value.  See
        // {'bdlat_typecategory'|Category Tags and Enumerators} for
        // documentation about category tags.  Return the value from the
        // invocation of 'accessor'.  The 'accessor' must be an accessor
        // functor.  See {'bdlat_typecategory'|'ACCESSOR' Functors} for the
        // requirements on 'accessor'.  The behavior is undefined if 'object'
        // contains a null value.

    template <class TYPE, class MANIPULATOR>
    static int manipulateValueByCategory(TYPE         *object,
                                         MANIPULATOR&  manipulator);
        // Invoke the specified 'manipulator' on the address of the value
        // stored in the specified "nullable" 'object' and on a prvalue of
        // the category tag type for the dynamic category of the value.  See
        // {'bdlat_typecategory'|Category Tags and Enumerators} for
        // documentation about category tags.  Return the value from the
        // invocation of 'manipulator'.  The 'manipulator' must be a
        // manipulator functor.  See
        // {'bdlat_typecategory'|'MANIPULATOR' Functors}
        // for the requirements on 'manipulator'.  The behavior is undefined if
        // 'object' contains a null value.
};

              // ================================================
              // class NullableValueUtil::AccessByCategoryAdapter
              // ================================================

template <class ACCESSOR>
class NullableValueUtil::AccessByCategoryAdapter {
    // See the class-level documentation of 'NullableValueUtil' for the
    // description of this component-private class template.

    // DATA
    ACCESSOR *d_accessor_p;
        // The 'accessor' attribute of this object.

  public:
    // CREATORS
    explicit AccessByCategoryAdapter(ACCESSOR *accessor);
        // Create an 'AccessByCategoryAdapter' object having the specified
        // 'accessor' attribute.

    // ACCESSORS
    template <class VALUE_TYPE>
    int operator()(const VALUE_TYPE& value) const;
        // Invoke the 'accessor' of this object with the specified 'value' and
        // a prvalue of the category tag type for its dynamic category.  Return
        // the value from the invocation of 'accessor'.
};

            // ====================================================
            // class NullableValueUtil::ManipulateByCategoryAdapter
            // ====================================================

template <class MANIPULATOR>
class NullableValueUtil::ManipulateByCategoryAdapter {
    // See the class-level documentation of 'NullableValueUtil' for the
    // description of this component-private class template.

    // DATA
    MANIPULATOR *d_manipulator_p;
        // The 'manipulator' attribute of this object.

  public:
    // CREATORS
    explicit ManipulateByCategoryAdapter(MANIPULATOR *manipulator);
        // Create a 'ManipulateByCategory' object having the specified
        // 'manipulator' attribute value.

    // ACCESSORS
    template <class VALUE_TYPE>
    int operator()(VALUE_TYPE *value) const;
        // Invoke the 'manipulator' of this object with the specified 'value'
        // and a prvalue of the category tag type for its dynamic category.
        // Return the value from the invocation of the 'manipulator'.
};

// ============================================================================
//                            INLINE DEFINITIONS
// ============================================================================

                          // -----------------------
                          // class NullableValueUtil
                          // -----------------------

// CLASS METHODS
template <class TYPE, class ACCESSOR>
inline
int NullableValueUtil::accessValueByCategory(const TYPE& object,
                                             ACCESSOR&   accessor)
{
#if !defined(BSLS_PLATFORM_CMP_SUN)
    BSLMF_ASSERT((bdlat_NullableValueFunctions::IsNullableValue<TYPE>::VALUE));
#endif
    BSLS_ASSERT(bdlat_TypeCategoryFunctions::select(object) ==
                bdlat_TypeCategory::e_NULLABLE_VALUE_CATEGORY);
    BSLS_ASSERT(!bdlat_NullableValueFunctions::isNull(object));

    const NullableValueUtil::AccessByCategoryAdapter<ACCESSOR> adapter(
                                                                    &accessor);
    return bdlat_NullableValueFunctions::accessValue(object, adapter);
}

template <class TYPE, class MANIPULATOR>
inline
int NullableValueUtil::manipulateValueByCategory(TYPE         *object,
                                                 MANIPULATOR&  manipulator)
{
#if !defined(BSLS_PLATFORM_CMP_SUN)
    BSLMF_ASSERT((bdlat_NullableValueFunctions::IsNullableValue<TYPE>::VALUE));
#endif
    BSLS_ASSERT(object);
    BSLS_ASSERT(bdlat_TypeCategoryFunctions::select(*object) ==
                bdlat_TypeCategory::e_NULLABLE_VALUE_CATEGORY);
    BSLS_ASSERT(!bdlat_NullableValueFunctions::isNull(*object));

    const NullableValueUtil::ManipulateByCategoryAdapter<MANIPULATOR> adapter(
                                                                 &manipulator);
    return bdlat_NullableValueFunctions::manipulateValue(object, adapter);
}

              // ------------------------------------------------
              // class NullableValueUtil::AccessByCategoryAdapter
              // ------------------------------------------------

// CREATORS
template <class ACCESSOR>
inline
NullableValueUtil::AccessByCategoryAdapter<ACCESSOR>::AccessByCategoryAdapter(
                                                            ACCESSOR *accessor)
: d_accessor_p(accessor)
{
}

// ACCESSORS
template <class ACCESSOR>
template <class VALUE_TYPE>
inline
int NullableValueUtil::AccessByCategoryAdapter<ACCESSOR>::operator()(
                                                 const VALUE_TYPE& value) const
{
    return bdlat_TypeCategoryUtil::accessByCategory(value, *d_accessor_p);
}

            // ----------------------------------------------------
            // class NullableValueUtil::ManipulateByCategoryAdapter
            // ----------------------------------------------------

// CREATORS
template <class MANIPULATOR>
inline
NullableValueUtil::ManipulateByCategoryAdapter<
    MANIPULATOR>::ManipulateByCategoryAdapter(MANIPULATOR *manipulator)
: d_manipulator_p(manipulator)
{
}

// ACCESSORS
template <class MANIPULATOR>
template <class VALUE_TYPE>
inline
int NullableValueUtil::ManipulateByCategoryAdapter<MANIPULATOR>::operator()(
                                                       VALUE_TYPE *value) const
{
    return bdlat_TypeCategoryUtil::manipulateByCategory(value,
                                                        *d_manipulator_p);
}

}  // close package namespace
}  // close enterprise namespace

#endif  // INCLUDED_BDLAT_NULLABLEVALUEUTIL

// ----------------------------------------------------------------------------
// Copyright 2022 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------