ENH: add UPstreamTraits support to map data types and opcodes

The front-end traits:

- UPstream_dataType trait:
  This wrapper is unwinds the type to check against base/alias, but also
  checks if it is a component aggregate of a supported UPstream data type.
  This will be that main entry point for usage.

- UPstream_opType trait:
  Provides a mapping of OpenFOAM ops to their MPI equivalent.
  The \c opcode_id is the corresponding internal representation.

The lower-level traits (not normally used within coding)

- UPstream_base_dataType trait:
  Tests true/false if the specified data type has an internal MPI equivalent.
  The \c datatype_id is the corresponding internal enumeration.
  Even if this tests as false, it will always return \c type_byte as the
  fallback for general contiguous data

- UPstream_alias_dataType trait:
  Provides mapping for <int/long/long long,...> to the fundamental 32/64
  integrals, since <int/long/long long,...> may not otherwise directly map
  on all systems.

NOTE: can use the updates Test-machine-sizes test application to
determine if all data types and aliases are properly defined on
different systems
This commit is contained in:
Mark Olesen 2025-02-25 09:52:20 +01:00
parent bf60a124ab
commit dccdb263e8
5 changed files with 765 additions and 6 deletions

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2018-2022 OpenCFD Ltd.
Copyright (C) 2018-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -25,9 +25,12 @@ License
Description
Test the sizeof for basic types.
Also tests how the data mapping of OpenFOAM types to UPstream (MPI)
type ids are handled.
Can be compiled and run without any OpenFOAM libraries.
g++ -std=c++11 -oTest-machine-sizes Test-machine-sizes.cpp
g++ -std=c++17 -oTest-machine-sizes Test-machine-sizes.cpp
\*---------------------------------------------------------------------------*/
@ -37,6 +40,114 @@ Description
#include <iostream>
#include <limits>
#include <typeinfo>
#include <type_traits>
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Partial copy from UPstream.H
//- Some MPI data types
//
//- Mapping of some fundamental and aggregate types to MPI data types
enum class dataTypes : int
{
// Builtin Types [8]:
DataTypes_begin, //!< Begin builtin types (internal use)
type_byte = DataTypes_begin, // also for char, unsigned char
type_int32,
type_int64,
type_uint32,
type_uint64,
type_float,
type_double,
type_long_double,
invalid
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Partial copy from UPstreamTraits.H
//- A supported UPstream data type (intrinsic or user-defined)
template<class T>
struct UPstream_base_dataType : std::false_type
{
static constexpr auto datatype_id = dataTypes::invalid;
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Specializations of the above,
// each to match the elements of UPstream::dataTypes
#undef defineUPstreamDataTraits
#define defineUPstreamDataTraits(TypeId, Type) \
template<> struct UPstream_base_dataType<Type> : std::true_type \
{ \
static constexpr auto datatype_id = dataTypes::TypeId; \
};
defineUPstreamDataTraits(type_byte, char);
defineUPstreamDataTraits(type_byte, unsigned char);
defineUPstreamDataTraits(type_int32, int32_t);
defineUPstreamDataTraits(type_int64, int64_t);
defineUPstreamDataTraits(type_uint32, uint32_t);
defineUPstreamDataTraits(type_uint64, uint64_t);
defineUPstreamDataTraits(type_float, float);
defineUPstreamDataTraits(type_double, double);
defineUPstreamDataTraits(type_long_double, long double);
#undef defineUPstreamDataTraits
//- Explicit handling of data type aliases. This is necessary since
//- different systems map things like 'unsigned long' differently but we
//- restrict ourselves to int32/int64 types
template<class T>
struct UPstream_alias_dataType
:
std::bool_constant
<
// Base type (no alias needed)
UPstream_base_dataType<std::remove_cv_t<T>>::value ||
(
// Or some int 32/64 type to re-map
std::is_integral_v<T>
&& (sizeof(T) == sizeof(int32_t) || sizeof(T) == sizeof(int64_t))
)
>
{
// Is it using the base type? (no alias needed)
static constexpr bool is_base =
UPstream_base_dataType<std::remove_cv_t<T>>::value;
using base = std::conditional_t
<
UPstream_base_dataType<std::remove_cv_t<T>>::value, // is_base
std::remove_cv_t<T>,
std::conditional_t
<
(
std::is_integral_v<T>
&& (sizeof(T) == sizeof(int32_t) || sizeof(T) == sizeof(int64_t))
),
std::conditional_t
<
(sizeof(T) == sizeof(int32_t)),
std::conditional_t<std::is_signed_v<T>, int32_t, uint32_t>,
std::conditional_t<std::is_signed_v<T>, int64_t, uint64_t>
>,
char // Fallback value (assuming it is contiguous)
>
>;
static constexpr auto datatype_id =
UPstream_base_dataType<base>::datatype_id;
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
template<class T>
void print(const char* name, bool showLimits = true)
@ -47,28 +158,78 @@ void print(const char* name, bool showLimits = true)
if (showLimits)
{
std::cout
<< " \"max\"=" << std::numeric_limits<T>::max();
<< " max=<";
if constexpr (sizeof(T) == 1)
{
std::cout << int(std::numeric_limits<T>::max());
}
else
{
std::cout << std::numeric_limits<T>::max();
}
std::cout << '>';
}
// A declared or deduced MPI type, or aliased
std::cout
<< " is_mpi=" << UPstream_base_dataType<T>::value
<< " (" << int(UPstream_base_dataType<T>::datatype_id) << ")";
if (UPstream_alias_dataType<T>::value)
{
if (UPstream_alias_dataType<T>::is_base)
{
std::cout<< " is_base";
}
else
{
std::cout<< " is_alias ("
<< int(UPstream_alias_dataType<T>::datatype_id) << ")";
}
}
else
{
std::cout<< " no_alias";
}
std::cout<< '\n';
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Main program:
int main(int argc, char *argv[])
{
std::cout<< "c++ = " << __cplusplus << '\n';
std::cout<< "machine sizes\n---\n\n";
std::cout<< "machine sizes (and some MPI traits)\n---\n\n";
print<int8_t>("int8_t");
print<uint8_t>("uint8_t");
print<int16_t>("int16_t");
print<uint16_t>("uint16_t");
print<int32_t>("int32_t");
print<uint32_t>("uint32_t");
print<int64_t>("int64_t");
print<uint64_t>("uint64_t");
std::cout << '\n';
print<char>("char");
print<unsigned char>("unsigned char");
print<short>("short");
print<int>("int");
print<unsigned>("unsigned");
print<long>("long");
print<unsigned long>("unsigned long");
print<std::size_t>("std::size_t");
print<long long>("long long");
std::cout << '\n';
print<std::size_t>("std::size_t");
print<std::streamsize>("std::streamsize");
std::cout << '\n';
print<float>("float");
print<double>("double");
print<long double>("long double");

View File

@ -0,0 +1,3 @@
Test-UPstreamTraits.cxx
EXE = $(FOAM_USER_APPBIN)/Test-UPstreamTraits

View File

@ -0,0 +1,2 @@
/* EXE_INC = */
/* EXE_LIBS = */

View File

@ -0,0 +1,267 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Description
Simple compilation tests and access for UPstream types
\*---------------------------------------------------------------------------*/
#include "pTraits.H"
#include "contiguous.H"
#include "FixedList.H"
#include "boolVector.H" // A FixedList pretending to be a vector
#include "barycentric.H"
#include "complex.H"
#include "vector.H"
#include "tensor.H"
#include "uLabel.H"
#include "Switch.H"
#include "IOstreams.H"
#include "UPstream.H"
#include <type_traits>
using namespace Foam;
// Just for debugging
const List<std::string> dataType_names
({
"byte",
"int32",
"int64",
"uint32",
"uint64",
"float",
"double",
"long_double",
"float(2)",
"double(2)",
"float(3)",
"double(3)",
"float(6)",
"double(6)",
"float(9)",
"double(9)"
});
//- Test for pTraits typeName member : default is false
template<class T, class = void>
struct check_has_typeName : std::false_type {};
//- Test for pTraits zero
template<class T>
struct check_has_typeName
<
T,
std::void_t<decltype(pTraits<std::remove_cv_t<T>>::typeName)>
>
:
std::true_type
{};
// Possible future change...
// //- A supported UPstream data type (intrinsic or user-defined)
// template<>
// struct UPstream_base_dataType<complex> : std::true_type
// {
// static constexpr auto datatype_id = []()
// {
// if constexpr (sizeof(complex) == 2*sizeof(float))
// return UPstream::dataTypes::type_2float;
// else
// return UPstream::dataTypes::type_2double;
// }();
// };
template<class T>
void printTypeName(const bool showSize = false)
{
// Both float and double have pTraits typeName = "scalar"!
if constexpr (std::is_same_v<float, std::remove_cv_t<T>>)
{
Info<< "<float>";
}
else if constexpr (std::is_same_v<double, std::remove_cv_t<T>>)
{
Info<< "<double>";
}
else if constexpr (check_has_typeName<T>::value)
{
Info<< pTraits<std::remove_cv_t<T>>::typeName;
}
else
{
Info<< typeid(T).name();
}
if (showSize)
{
Info<< " (" << sizeof(T) << " bytes)";
}
}
template<class Type, bool UseTypeName = true>
void printPstreamTraits(const std::string_view name = std::string_view())
{
Info<< "========" << nl;
Info<< "type: ";
if (!name.empty())
{
Info<< name << ' ';
}
if constexpr (UseTypeName)
{
printTypeName<Type>(true);
}
else
{
Info<< typeid(Type).name();
Info<< " (" << sizeof(Type) << " bytes)";
}
Info<< ", cmpt:";
printTypeName<typename Foam::pTraits_cmptType<Type>::type>(true);
Info<< nl
<< " is_contiguous:"
<< is_contiguous<Type>::value
<< ", is base:"
<< UPstream_base_dataType<Type>::value
<< ", is cmpt:"
<< UPstream_dataType<Type>::value << nl;
Info<< "is base:"
<< UPstream_base_dataType<Type>::value
<< " (type:" << int(UPstream_base_dataType<Type>::datatype_id)
<< ") is alias:" << UPstream_alias_dataType<Type>::value
<< " (type:" << int(UPstream_alias_dataType<Type>::datatype_id)
<< ")" << nl;
{
int index = int(UPstream_base_dataType<Type>::datatype_id);
Info<< "datatype: " << index;
if (index < dataType_names.size())
{
Info<< ' ' << dataType_names[index];
}
Info<< nl;
}
{
// Use element or component type (or byte-wise) for data type
using base = typename UPstream_dataType<Type>::base;
constexpr auto datatype = UPstream_dataType<Type>::datatype_id;
Info<< "datatype => ";
printTypeName<base>();
Info<< " (" << sizeof(Type)/sizeof(base) << " elems)" << nl
<< "datatype: " << static_cast<int>(datatype) << nl;
}
}
template<class BinaryOp>
void printOpCodeTraits(BinaryOp bop, std::string_view name)
{
Info<< "op: " << name << ' ';
if constexpr (UPstream_opType<BinaryOp>::value)
{
Info<< "supported";
}
else
{
Info<< "unknown";
}
Info<< ": " << int(UPstream_opType<BinaryOp>::opcode_id) << nl;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Main program:
int main()
{
printPstreamTraits<bool>();
printPstreamTraits<label>();
printPstreamTraits<int>("<int>");
printPstreamTraits<long>("<long>");
printPstreamTraits<unsigned>("<unsigned>");
printPstreamTraits<unsigned int>("<unsigned int>");
printPstreamTraits<unsigned long>("<long long>");
printPstreamTraits<const float>();
printPstreamTraits<floatVector>();
printPstreamTraits<scalar>();
printPstreamTraits<double>();
printPstreamTraits<doubleVector>();
// Avoid typeName for barycentric. It is declared, but not defined
printPstreamTraits<barycentric, false>("barycentric");
printPstreamTraits<complex>(); // Uses specialized pTraits_...
printPstreamTraits<boolVector>(); // Uses specialized pTraits_...
printPstreamTraits<floatVector>();
printPstreamTraits<doubleVector>();
printPstreamTraits<tensor>();
printPstreamTraits<word>();
printPstreamTraits<std::string>();
// This will not identify properly at the moment...
printPstreamTraits<FixedList<doubleVector, 4>>();
printPstreamTraits<labelPair>();
Info<< nl
<< "========" << nl
<< "Mapping of binary reduction ops" << nl;
printOpCodeTraits(minOp<scalar>{}, "min");
printOpCodeTraits(maxOp<vector>{}, "max");
printOpCodeTraits(sumOp<vector>{}, "sum");
printOpCodeTraits(plusOp<vector>{}, "plus");
printOpCodeTraits(multiplyOp<scalar>{}, "multiply");
printOpCodeTraits(divideOp<vector>{}, "divide");
printOpCodeTraits(minMagSqrOp<vector>{}, "minMagSqr");
printOpCodeTraits(bitAndOp<vector>{}, "bitAnd<vector>");
printOpCodeTraits(bitOrOp<vector>{}, "bitOr<vector>");
printOpCodeTraits(bitOrOp<float>{}, "bitOr<float>");
printOpCodeTraits(bitAndOp<unsigned>{}, "bitAnd<unsigned>");
printOpCodeTraits(bitOrOp<unsigned>{}, "bitOr<unsigned>");
Info<< nl << "End\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -26,7 +26,28 @@ License
Description
A set of traits associated with UPstream communication
SourceFiles
- UPstream_dataType trait:
This wrapper is unwinds the type to check against base/alias, but also
checks if it is a component aggregate of a supported UPstream data type.
This will be that main entry point for usage.
- UPstream_opType trait:
Provides a mapping of OpenFOAM ops to their MPI equivalent.
The \c opcode_id is the corresponding internal representation.
Note
Additional helper traits:
- UPstream_base_dataType trait:
Tests true/false if the specified data type has an internal
MPI equivalent. The \c datatype_id is the corresponding
internal enumeration. Even if this tests as false, it will
always return \c type_byte as the fallback for general contiguous data
- UPstream_alias_dataType trait:
Provides mapping for <int/long/long long,...> to the fundamental
32/64 bit integrals, since <int/long/long long,...> may not otherwise
directly map on all systems.
\*---------------------------------------------------------------------------*/
@ -36,9 +57,314 @@ SourceFiles
#include "UPstream.H"
#include <cstdint>
#include <ios> // For streamsize
#include <type_traits>
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
// Forward Declarations
// Some vector-space types
// -----------------------
//! \cond
template<class T> class Vector;
template<class T> class SymmTensor;
template<class T> class Tensor;
//! \endcond
// -------------------------
// Some binary operators (as per ops.H), but since ListOps.H is included
// by UPstream.H, don't need to forward declare
// -------------------------
// template<class T> struct minOp;
// template<class T> struct maxOp;
// template<class T> struct plusOp;
// template<class T> struct sumOp;
// template<class T> struct multiplyOp;
// template<class T> struct bitAndOp;
// template<class T> struct bitOrOp;
// template<class T> struct bitXorOp;
//! \cond
template<class T> struct UPstream_dataType;
template<class T> struct UPstream_opType;
//! \endcond
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Base traits
//- A supported UPstream (MPI) reduce/window operation type
template<class T>
struct UPstream_opType : std::false_type
{
static constexpr auto opcode_id = UPstream::opCodes::invalid;
};
//- A supported UPstream data type (intrinsic or user-defined)
template<class T>
struct UPstream_base_dataType : std::false_type
{
static constexpr auto datatype_id = UPstream::dataTypes::invalid;
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Trait specializations (op-codes)
//- Map minOp\<T\> to \c UPstream::opCodes::op_min
template<class T>
struct UPstream_opType<Foam::minOp<T>> : std::true_type
{
static constexpr auto opcode_id = UPstream::opCodes::op_min;
};
//- Map maxOp\<T\> to \c UPstream::opCodes::op_max
template<class T>
struct UPstream_opType<Foam::maxOp<T>> : std::true_type
{
static constexpr auto opcode_id = UPstream::opCodes::op_max;
};
//- Map sumOp\<T\> to \c UPstream::opCodes::op_sum
template<class T>
struct UPstream_opType<Foam::sumOp<T>> : std::true_type
{
static constexpr auto opcode_id = UPstream::opCodes::op_sum;
};
//- Map plusOp\<T\> to \c UPstream::opCodes::op_sum
//- as a recognized alternative to sumOp\<T\>
template<class T>
struct UPstream_opType<Foam::plusOp<T>> : std::true_type
{
static constexpr auto opcode_id = UPstream::opCodes::op_sum;
};
//- Map multiplyOp\<T\> to \c UPstream::opCodes::op_prod
template<class T>
struct UPstream_opType<Foam::multiplyOp<T>> : std::true_type
{
static constexpr auto opcode_id = UPstream::opCodes::op_prod;
};
// NOTE (2025-02):
// currently no mappings provided for
// (op_bool_and, op_bool_or, op_bool_xor) until the calling semantics
// have been properly defined
// These are only viable for unsigned integral types,
// probably not for signed integral types.
// Be extra restrictive for now
//- Map bitAndOp\<T\> to \c UPstream::opCodes::op_bit_and
//- (for unsigned integrals)
template<class T>
struct UPstream_opType<Foam::bitAndOp<T>>
:
// ie, std::unsigned_integral<T> concept
std::bool_constant<std::is_integral_v<T> && !std::is_signed_v<T>>
{
static constexpr auto opcode_id = []() constexpr noexcept
{
if constexpr (std::is_integral_v<T> && !std::is_signed_v<T>)
return UPstream::opCodes::op_bit_and;
else
return UPstream::opCodes::invalid;
}();
};
//- Map bitOrOp\<T\> to \c UPstream::opCodes::op_bit_or
//- (for unsigned integrals)
template<class T>
struct UPstream_opType<Foam::bitOrOp<T>>
:
// ie, std::unsigned_integral<T> concept
std::bool_constant<std::is_integral_v<T> && !std::is_signed_v<T>>
{
static constexpr auto opcode_id = []() constexpr noexcept
{
if constexpr (std::is_integral_v<T> && !std::is_signed_v<T>)
return UPstream::opCodes::op_bit_or;
else
return UPstream::opCodes::invalid;
}();
};
//- Map bitXorOp\<T\> to \c UPstream::opCodes::op_bit_xor
//- (for unsigned integrals)
template<class T>
struct UPstream_opType<Foam::bitXorOp<T>>
:
// ie, std::unsigned_integral<T> concept
std::bool_constant<std::is_integral_v<T> && !std::is_signed_v<T>>
{
static constexpr auto opcode_id = []() constexpr noexcept
{
if constexpr (std::is_integral_v<T> && !std::is_signed_v<T>)
return UPstream::opCodes::op_bit_xor;
else
return UPstream::opCodes::invalid;
}();
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Trait specializations (data types)
// Specializations to match elements of UPstream::dataTypes
#undef defineUPstreamDataTraits
#define defineUPstreamDataTraits(TypeId, Type) \
\
/*! \brief Map \c Type to UPstream::dataTypes::TypeId */ \
template<> struct UPstream_base_dataType<Type> : std::true_type \
{ \
static constexpr auto datatype_id = UPstream::dataTypes::TypeId; \
}; \
/*! \brief Map \c const \c Type to \c UPstream::dataTypes::TypeId */ \
template<> struct UPstream_base_dataType<const Type> : std::true_type \
{ \
static constexpr auto datatype_id = UPstream::dataTypes::TypeId; \
};
// Intrinsic Types [8]:
// Note: uses 'int32_t,int64_t,...' instead of 'int,long,...' to minimize
// the possibility of duplicates types.
// OpenFOAM defines Foam::label as either int32_t,int64_t (not int,long) too.
defineUPstreamDataTraits(type_byte, char);
defineUPstreamDataTraits(type_byte, unsigned char);
defineUPstreamDataTraits(type_int32, int32_t);
defineUPstreamDataTraits(type_int64, int64_t);
defineUPstreamDataTraits(type_uint32, uint32_t);
defineUPstreamDataTraits(type_uint64, uint64_t);
defineUPstreamDataTraits(type_float, float);
defineUPstreamDataTraits(type_double, double);
defineUPstreamDataTraits(type_long_double, long double);
// User Types [6]:
defineUPstreamDataTraits(type_3float, Vector<float>);
defineUPstreamDataTraits(type_3double, Vector<double>);
defineUPstreamDataTraits(type_6float, SymmTensor<float>);
defineUPstreamDataTraits(type_6double, SymmTensor<double>);
defineUPstreamDataTraits(type_9float, Tensor<float>);
defineUPstreamDataTraits(type_9double, Tensor<double>);
#undef defineUPstreamDataTraits
// ------------------------------------------------------------------------- //
//- Explicit handling of data type aliases. This is necessary since
//- different systems map things like 'unsigned long' differently but we
//- restrict ourselves to int32/int64 types
template<class T>
struct UPstream_alias_dataType
:
std::bool_constant
<
// Base type (no alias needed)
UPstream_base_dataType<std::remove_cv_t<T>>::value ||
(
// Or some int 32/64 type to re-map
std::is_integral_v<T>
&& (sizeof(T) == sizeof(int32_t) || sizeof(T) == sizeof(int64_t))
)
>
{
// Is it using the base type? (no alias needed)
static constexpr bool is_base =
UPstream_base_dataType<std::remove_cv_t<T>>::value;
using base = std::conditional_t
<
UPstream_base_dataType<std::remove_cv_t<T>>::value,
std::remove_cv_t<T>, // <- using base
std::conditional_t // <- using alias
<
(
std::is_integral_v<T>
&& (sizeof(T) == sizeof(int32_t) || sizeof(T) == sizeof(int64_t))
),
std::conditional_t
<
(sizeof(T) == sizeof(int32_t)),
std::conditional_t<std::is_signed_v<T>, int32_t, uint32_t>,
std::conditional_t<std::is_signed_v<T>, int64_t, uint64_t>
>,
char // Fallback is a byte (eg, arbitrary contiguous data)
>
>;
static constexpr auto datatype_id =
UPstream_base_dataType<base>::datatype_id;
};
// ------------------------------------------------------------------------- //
//- A supported UPstream data type (fundamental or user-defined)
//- or a component aggregate of a supported UPstream data type.
//
// Is true for the following conditions:
// - The \c Type is directly supported
// - The \c cmptType (eg, from VectorSpace) exists and is directly supported
// - Fallback to byte-wise representation (ie, for contiguous)
// .
template<class T>
struct UPstream_dataType
:
std::bool_constant
<
UPstream_alias_dataType<T>::value
|| UPstream_alias_dataType<typename pTraits_cmptType<T>::type>::value
>
{
// Is it using the base type? (ie, not using components)
static constexpr bool is_base = UPstream_alias_dataType<T>::value;
//- The underlying data type (if supported) or byte
using base = std::conditional_t
<
UPstream_alias_dataType<T>::value,
typename UPstream_alias_dataType<T>::base, // <- using base
typename UPstream_alias_dataType
<typename pTraits_cmptType<T>::type>::base // <- using components
>;
//- The corresponding UPstream::dataTypes enumeration
static constexpr auto datatype_id =
UPstream_base_dataType<base>::datatype_id;
//- The size in terms of the number of underlying data elements
static std::streamsize size(std::streamsize count) noexcept
{
if constexpr (UPstream_alias_dataType<T>::value)
{
// using base: no multiplier
return count;
}
else
{
// using components: with multiplier
return count*(sizeof(T)/sizeof(base));
}
}
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif