// bdlat_arrayutil.h                                                  -*-C++-*-
#ifndef INCLUDED_BDLAT_ARRAYUTIL
#define INCLUDED_BDLAT_ARRAYUTIL

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

//@PURPOSE: Provide utilities for operating on 'bdlat' "array" types.
//
//@CLASSES:
//  bdlat::ArrayUtil: namespace for utility functions on "array" types
//
//@SEE_ALSO: bdlat_arrayfunctions, bdlat_typecategory
//
//@DESCRIPTION: This component provides a utility 'struct', 'bdlat::ArrayUtil',
// which serves as a namespace for a collection of function templates providing
// derived operations for "array" types.  See {'bdlat_arrayfunctions'}
// for the set of requirements of "array" types in the 'bdlat' framework.  See
// {'bdlat_typecategory'} for more general information about this framework.
//
///Primitive and Derived Functions of Arrays
///-----------------------------------------
// In order to be "plugged in" to the 'bdlat' framework as an "array", 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_arrayfunctions'} component.  We call the required
// function overloads the "primitive" operations of "array" 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 "array" type.
//
///Usage
///-----
// In this section we show intended usage of this component.
//
///Example 1: Accessing an Array Element And Its Category
/// - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Suppose we would like to define a function that detects whether an element
// of an array is itself an array, in order to more generally detect nested
// arrays.
//
// First, we need to define an accessor functor per
// {'bdlat_typecategory'|'ACCESSOR' Functors} that will be used to detect
// whether an array element is itself 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', 'MyArrayUtil', that provides a
// function for detecting whether or not an array has an element that is itself
// an array:
//..
//  struct MyArrayUtil {
//
//      // CLASS METHODS
//      template <class TYPE>
//      static int isElementAnArray(bool        *isArray,
//                                  const TYPE&  array,
//                                  int          index)
//          // Load the value 'true' to the specified 'isArray' if the element
//          // at the specified 'index' of the specified 'array' 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 unless the specified
//          // 'array' has the "array" type category, '0 <= index', and
//          // 'index < bdlat_ArrayFunctions::size(array)'.
//      {
//          BSLS_ASSERT(bdlat_TypeCategoryFunctions::select(array) ==
//                      bdlat_TypeCategory::e_ARRAY_CATEGORY);
//          BSLS_ASSERT(0 <= index);
//          BSLS_ASSERT(static_cast<bsl::size_t>(index) <
//                      bdlat_ArrayFunctions::size(array));
//
//          MyArrayDetector detector;
//          int rc = bdlat::ArrayUtil::accessElementByCategory(array,
//                                                             detector,
//                                                             index);
//          if (0 != rc) {
//              return -1;                                            // RETURN
//          }
//
//          *isArray = detector.didVisitArray();
//          return 0;
//      }
//  };
//..
// Finally, we can use this utility to detect whether elements of array types
// are themselves arrays:
//..
//  void example()
//  {
//      bsl::vector<int> vectorA;
//      vectorA.push_back(42);
//
//      bool isArray = false;
//      int rc = MyArrayUtil::isElementAnArray(&isArray, vectorA, 0);
//
//      assert(0 == rc);
//      assert(! isArray);
//
//      bsl::vector<bsl::vector<int> > vectorB;
//      vectorB.push_back(bsl::vector<int>());
//
//      rc = MyArrayUtil::isElementAnArray(&isArray, vectorB, 0);
//
//      assert(0 == rc);
//      assert(isArray);
//  }
//..

#include <bdlscm_version.h>

#include <bdlat_arrayfunctions.h>
#include <bdlat_typecategory.h>

#include <bslmf_assert.h>

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

#include <bsl_cstddef.h>

namespace BloombergLP {
namespace bdlat {

                              // ================
                              // struct ArrayUtil
                              // ================

struct ArrayUtil {
    // This 'struct' provides a namespace for suite of function templates
    // providing non-primitive operations on "array" 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_ArrayFunctions::accessElement', 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_ArrayFunctions::manipulateElement', for example.

  public:
    // CLASS METHODS
    template <class TYPE, class ACCESSOR>
    static int accessElementByCategory(const TYPE& array,
                                       ACCESSOR&   accessor,
                                       int         index);
        // Invoke the specified 'accessor' on the non-modifiable element at the
        // specified 'index' of the specified 'array' and on a prvalue of the
        // category tag type for the dynamic category of the element.  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 unless
        // '0 <= index < bdlat_ArrayFunctions::size(array)'.

    template <class TYPE, class MANIPULATOR>
    static int manipulateElementByCategory(TYPE         *array,
                                           MANIPULATOR&  manipulator,
                                           int           index);
        // Invoke the specified 'manipulator' on the address of the element at
        // the specified 'index' of the specified 'array' and on a prvalue of
        // the category tag type for the dynamic category of the element.  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 unless
        // '0 <= index < bdlat_ArrayFunctions::size(array)'.
};

                  // ========================================
                  // class ArrayUtil::AccessByCategoryAdapter
                  // ========================================

template <class ACCESSOR>
class ArrayUtil::AccessByCategoryAdapter {
    // See the class-level documentation of 'ArrayUtil' 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 value.

    // 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 the 'accessor'.
};

                // ============================================
                // class ArrayUtil::ManipulateByCategoryAdapter
                // ============================================

template <class MANIPULATOR>
class ArrayUtil::ManipulateByCategoryAdapter {
    // See the class-level documentation of 'ArrayUtil' 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
// ============================================================================

                              // ----------------
                              // struct ArrayUtil
                              // ----------------

// CLASS METHODS
template <class TYPE, class ACCESSOR>
inline
int ArrayUtil::accessElementByCategory(const TYPE& array,
                                       ACCESSOR&   accessor,
                                       int         index)
{
#if !defined(BSLS_PLATFORM_CMP_SUN)
    BSLMF_ASSERT((bdlat_ArrayFunctions::IsArray<TYPE>::VALUE));
#endif
    BSLS_ASSERT(bdlat_TypeCategoryFunctions::select(array) ==
                bdlat_TypeCategory::e_ARRAY_CATEGORY);
    BSLS_ASSERT(0 <= index);
    BSLS_ASSERT(static_cast<bsl::size_t>(index) <
                bdlat_ArrayFunctions::size(array));

    const AccessByCategoryAdapter<ACCESSOR> adapter(&accessor);
    return bdlat_ArrayFunctions::accessElement(array, adapter, index);
}

template <class TYPE, class MANIPULATOR>
inline
int ArrayUtil::manipulateElementByCategory(TYPE         *array,
                                           MANIPULATOR&  manipulator,
                                           int           index)
{
#if !defined(BSLS_PLATFORM_CMP_SUN)
    BSLMF_ASSERT((bdlat_ArrayFunctions::IsArray<TYPE>::VALUE));
#endif
    BSLS_ASSERT(array);
    BSLS_ASSERT(bdlat_TypeCategoryFunctions::select(*array) ==
                bdlat_TypeCategory::e_ARRAY_CATEGORY);
    BSLS_ASSERT(0 <= index);
    BSLS_ASSERT(static_cast<bsl::size_t>(index) <
                bdlat_ArrayFunctions::size(*array));

    const ManipulateByCategoryAdapter<MANIPULATOR> adapter(&manipulator);
    return bdlat_ArrayFunctions::manipulateElement(array, adapter, index);
}

                  // ----------------------------------------
                  // class ArrayUtil::AccessByCategoryAdapter
                  // ----------------------------------------

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

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

                // --------------------------------------------
                // class ArrayUtil::ManipulateByCategoryAdapter
                // --------------------------------------------

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

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

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

#endif  // INCLUDED_BDLAT_ARRAYUTIL

// ----------------------------------------------------------------------------
// 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 ----------------------------------