For historical reasons the standard library generally uses size_t
, which is an unsigned type, as the result type of size functions. One exception is std::count
. But std::string::size()
is among the size_t
functions,
Since the language guarantees modular arithmetic for unsigned type expressions, you get wrap-around where -1
becomes the largest value of the unsigned type.
Another and IMO more obviously silly consequence is that
std::string( "Hello" ).size() < -1
is guaranteed. Just pure nonsense as source code text, when one recalls that the purpose of high level source code is to communicate more clearly to humans.
To avoid this mess you can define a logical signed Size
type as an alias for ptrdiff_t
(which is the signed type counterpart to size_t
) and ditto size functions static_size
, n_items
and length
, plus a checker is_empty
, as follows:
sizes_and_lengths.hpp
#pragma once
// p/cppx/core_language_support/sizes_and_lengths.hpp
// Copyright © Alf P. Steinbach 2015. Boost Software License 1.0.
#include <p/cppx/core_language_support/basic_types.hpp>
#include <p/cppx/core_language_support/overloading.hpp> // cppx::(Object_argument etc.)
#include <p/cppx/core_language_support/type_builders.hpp> // cppx::(Ptr_, Ref_, Array_of_, ...)
#include <p/cppx/core_language_support/tmp/If_.hpp> // cppx::(If_, Is_)
#include <p/cppx/core_language_support/tmp/type_relationships.hpp> // cppx::Is_convertible_to_
#include <string> // std::basic_string
#include <utility> // std::declval
namespace progrock{ namespace cppx{
// These templates use size_t to accommodate g++, which has bug in this area.
// static_size()
// Static capacity of a collection.
template< class Type, size_t n >
constexpr
auto static_size( In_ref_<Raw_array_of_<n, Type>> )
-> Size
{ return n; }
template< class Type, size_t n >
constexpr
auto static_size( In_ref_<Array_of_<n, Type>> )
-> Size
{ return n; }
template< size_t n >
constexpr
auto static_size( In_ref_<std::bitset<n>> )
-> Size
{ return n; }
// n_items() & is_empty()
// Right-typed (signed integer) dynamic size of a collection.
template< class Type >
auto n_items( In_ref_<Type> o )
-> Size
{ return o.size(); }
template< size_t n >
auto n_items( In_ref_<std::bitset<n>> o )
-> Size
{ return o.count(); } // Corresponds to std::set<int>::size()
namespace impl {
using std::declval;
template< class Type >
constexpr
auto has_std_empty_checker( ... )
-> bool
{ return false; }
template< class Type >
constexpr
auto has_std_empty_checker( int )
-> If_<
Is_<bool, decltype( declval<const Type>().empty() )>, // CFINAE
Then_<bool> // SFINAE
>
{ return true; }
struct Using_empty_method
{
template< class Type >
auto is_empty( In_ref_<Type> o ) const
-> bool
{ return o.empty(); }
};
struct Using_n_items_function
{
template< class Type >
auto is_empty( In_ref_<Type> o ) const
-> bool
{ return (n_items( o ) == 0); }
};
} // namespace impl
template< class Type >
constexpr
auto has_std_empty_checker()
-> bool
{ return impl::has_std_empty_checker<Type>( 42 ); }
template< class Type >
auto is_empty( In_ref_<Type> o )
-> bool
{
using Impl = Ifc_<
has_std_empty_checker<Type>(),
Then_<impl::Using_empty_method>,
Else_<impl::Using_n_items_function>
>;
return Impl().is_empty( o );
}
template< size_t n >
auto is_empty( In_ref_<std::bitset<n>> bits )
-> bool
{ return bits.none(); }
// length() & length_of_literal() & is_empty
// Lengths of strings.
template< class Char, size_t n >
constexpr
auto length_of_literal( In_ref_<Raw_array_of_<n, Char>> ) // "length" wraps this.
-> Size
{ return n - 1; }
template< class Type >
auto length( Object_argument, In_ref_<Type> s )
-> Size
{ return s.length(); }
template< class Char
, class Enabled_ = If_< Is_in_< Character_types, Char > >
>
auto length( Pointer_argument, In_value_<Ptr_<const Char>> s )
-> Size
{
auto p = s;
while( *p != 0 ) { ++p; }
return p - s;
}
template< class Char, size_t n
, class Enabled_ = If_< Is_in_< Character_types, Char > >
>
auto constexpr length( Array_argument, In_ref_<Raw_array_of_<n, Char>> a )
-> Size
{ return length_of_literal( a ); }
template< class Type >
auto length( In_ref_<Type> o )
-> Size
{ return length( Arg_kind_<Type>(), o ); }
template< class Char
, class Enabled_ = If_< Is_in_< Character_types, Char > >
>
auto is_empty( In_value_<Ptr_<const Char>> s )
-> bool
{ return (*s == 0); }
}} // namespace progrock::cppx
If you want to use this code, in part or in whole, directly without bringing in the supporting files from the cppx library, you'll have to translate e.g. In_
to the type you prefer for a logical in-argument, and so on. The cppx library is just a thing very much in-progress, but it's usable as-is already. It provides stuff such as the above, the very basics that somehow missed becoming part of the standard library, or even being proposed.