Skip to content

Commit

Permalink
For enums, use to_string, from_string. Requires C++17.
Browse files Browse the repository at this point in the history
  • Loading branch information
mgates3 committed Feb 13, 2024
1 parent f389f09 commit 83719b1
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 50 deletions.
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git")
testsweeper PRIVATE TESTSWEEPER_ID="${testsweeper_id}" )
endif()

# Use and export -std=c++11; don't allow -std=gnu++11 extensions.
target_compile_features( testsweeper PUBLIC cxx_std_11 )
# Use and export -std=c++17; don't allow -std=gnu++17 extensions.
target_compile_features( testsweeper PUBLIC cxx_std_17 )
set_target_properties( testsweeper PROPERTIES CXX_EXTENSIONS false )

if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.15)
Expand Down
2 changes: 1 addition & 1 deletion test/ref/101.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

Error: --type: invalid value 'x'
Error: --type: invalid datatype 'x'
TestSweeper version NA, id NA
input: ./tester --type 's,x,d' sort
Usage: test [-h|--help]
Expand Down
15 changes: 11 additions & 4 deletions test/test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,21 @@ Params::Params():
cache ( "cache", 0, ParamType::Value, 20, 1, 1024, "total cache size, in MiB" ),

// ----- routine parameters
// name, w, p, type, default, char2enum, enum2char, enum2str, help
datatype_old
// name, w, p, type, default, help
datatype ( "type", 4, ParamType::List, DataType::Double,
"One of: s, r32, single, float; d, r64, double; c, c32, complex<float>; z, c64, complex<double>; i, int, integer" ),

#ifdef DEPRECATED
// name, w, p, type, default, char2enum, enum2char, enum2str, help
datatype_old
( "type-old", 4, ParamType::List, DataType::Double, char2datatype, datatype2char, datatype2str,
"s=single (float), d=double, c=complex<float>, z=complex<double>, i=int" ),

// name, w, p, type, default, str2enum, enum2str, help
datatype ( "type", 4, ParamType::List, DataType::Double, str2datatype, datatype2str,
// name, w, p, type, default, str2enum, enum2str, help
datatype_old2
( "type", 4, ParamType::List, DataType::Double, str2datatype, datatype2str,
"One of: s, r32, single, float; d, r64, double; c, c32, complex<float>; z, c64, complex<double>; i, int, integer" ),
#endif

// name, w, type, default, min, max, help
nb ( "nb", 3, ParamType::List, 32, 0, INT64_MAX, "block size" ),
Expand Down
5 changes: 4 additions & 1 deletion test/test.hh
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ public:
testsweeper::ParamInt cache;

// ----- routine parameters
testsweeper::ParamEnum< testsweeper::DataType > datatype_old;
testsweeper::ParamEnum< testsweeper::DataType > datatype;
#ifdef DEPRECATED
testsweeper::ParamEnum< testsweeper::DataType > datatype_old;
testsweeper::ParamEnum< testsweeper::DataType > datatype_old2;
#endif
testsweeper::ParamInt nb;
testsweeper::ParamInt3 dim;
testsweeper::ParamInt3 grid;
Expand Down
228 changes: 186 additions & 42 deletions testsweeper.hh
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <limits>
#include <algorithm>
#include <complex>
#include <type_traits>

// Version is updated by make_release.py; DO NOT EDIT.
// Version 2023.11.05
Expand Down Expand Up @@ -53,6 +54,10 @@ public:
// -----------------------------------------------------------------------------
void throw_error( const char* format, ... );


// =============================================================================
// Enums

// -----------------------------------------------------------------------------
enum class DataType {
Integer = 'i',
Expand All @@ -63,32 +68,23 @@ enum class DataType {
DoubleComplex = 'z',
};

// ----------------------------------------
// Accepts: s (single/float), d (double), c (complex-single), z (complex-double),
// i (int)
inline DataType char2datatype( char ch )
{
ch = tolower( ch );
if (ch != 'i' && ch != 'h' && ch != 's' && ch != 'd' && ch != 'c' && ch != 'z') {
throw_error( "invalid value '%c'", ch );
}
return DataType( ch );
}

// ----------------------------------------
inline char datatype2char( DataType en )
{
return char( en );
}

// ----------------------------------------
/// Accepts a variety of inputs:
/// { i, i32, int, integer } => int
/// { s, r32, float, single } => float
/// { d, r64, double } => double
/// { c, c32, complex<float>, complex-float, complex-single } => complex<float>
/// { z, c64, complex<double>, complex-double } => complex<double>
inline DataType str2datatype( const char* str )
//----------------------------------------
/// Convert string to DataType enum. Accepts a variety of inputs:
///
/// { i, i32, int, integer } => Integer
///
/// { h, r16, half } => Half
/// { s, r32, float, single } => Float
/// { d, r64, double } => Double
/// { c, c32, complex<float> } => FloatComplex
/// { z, c64, complex<double> } => DoubleComplex
///
/// Single letters come from traditional BLAS names (i, s, d, c, z)
/// and a few commonly accepted new ones (h, q).
/// The r32, c32, etc. come from XBLAS proposal,
/// https://bit.ly/blas-g2-proposal
///
inline DataType from_string( std::string const& str, DataType dummy )
{
std::string str_ = str;
if (str_ == "i"
Expand All @@ -114,26 +110,138 @@ inline DataType str2datatype( const char* str )
|| str_ == "c64"
|| str_ == "complex<double>"
|| str_ == "complex-double") return DataType::DoubleComplex;
else {
throw_error( "invalid value '%s'", str );
return DataType(0);
else
throw_error( "invalid datatype '%s'", str.c_str() );
return DataType::Integer;
}

//--------------------
[[deprecated("Use from_string. Remove 2025-02.")]]
inline DataType char2datatype( char ch )
{
ch = tolower( ch );
if (ch != 'i' && ch != 's' && ch != 'd' && ch != 'c' && ch != 'z') {
throw_error( "invalid datatype '%c'", ch );
}
return DataType( ch );
}

//--------------------
[[deprecated("Use from_string. Remove 2025-02.")]]
inline DataType str2datatype( const char* str )
{
return from_string( str, DataType() );
}

//----------------------------------------
///
inline const char* to_c_str( DataType value )
{
switch (value) {
case DataType::Integer: return "i";
case DataType::Half: return "h";
case DataType::Single: return "s";
case DataType::Double: return "d";
case DataType::SingleComplex: return "c";
case DataType::DoubleComplex: return "z";
}
throw_error( "invalid datatype" );
}

// ----------------------------------------
inline const char* datatype2str( DataType en )
//----------------------------------------
/// Convert DataType enum to C-style string representation.
/// Temporary common low-level implementation for to_string and datatype2str.
///
inline const char* to_c_string( DataType value )
{
switch (en) {
switch (value) {
case DataType::Integer: return "i";
case DataType::Half: return "h";
case DataType::Single: return "s";
case DataType::Double: return "d";
case DataType::SingleComplex: return "c";
case DataType::DoubleComplex: return "z";
}
return "";
throw_error( "invalid datatype" );
return "?";
}

//--------------------
/// Convert DataType enum to C++ string representation.
///
inline std::string to_string( DataType value )
{
return std::string( to_c_string( value ) );
}

//--------------------
[[deprecated("Use to_string. Remove 2025-02.")]]
inline const char* datatype2str( DataType value )
{
// Can't write datatype2str using return to_string( value ).c_str()
// since string is on stack.
return to_c_string( value );
}

//--------------------
[[deprecated("Use to_string. Remove 2025-02.")]]
inline char datatype2char( DataType value )
{
return to_c_string( value )[ 0 ];
}


//==============================================================================
// Utilities

//------------------------------------------------------------------------------
/// Use SFINAE to test for existence of from_string( string, T* ) function.
template <typename T>
class has_from_string
{
private:
/// Matches from_string( string str, T* val ); return type is void.
template <typename T2>
static auto test( std::string str, T2 val )
-> decltype( from_string( str, T2() ) );

/// Matches everything else; return type is void (something other than T).
template <typename T2>
static void test(...);

public:
// True if from_string( string, type ) exists, based on void return type.
static const bool value = std::is_same<
T,
decltype( test<T>( std::string(), T() ) )
>::value;
};

//------------------------------------------------------------------------------
/// Use SFINAE to test for existence of to_string( T ) function.
/// This relies on ADL; it doesn't check the std namespace, so it won't
/// work for basic types (int, float, etc.) -- not sure how to do that.
template <typename T>
class has_to_string
{
private:
/// Matches to_string( T val ); return type is string.
template <typename T2>
static auto test( T2 val )
-> decltype( to_string( val ) );

/// Matches everything else; return type is int (something other than string).
template <typename>
static int test(...);

public:
// True if from_string( string, type ) exists, based on string return type.
static const bool value = std::is_same<
std::string,
decltype( test<T>( T() ) )
>::value;
};

// -----------------------------------------------------------------------------
int scan_range( const char **strp, int64_t *start, int64_t *end, int64_t *step );
int scan_range( const char **strp, double *start, double *end, double *step );
Expand Down Expand Up @@ -650,8 +758,19 @@ public:
typedef ENUM (*str2enum)( const char* str );
typedef const char* (*enum2str)( ENUM en );

// deprecated
// Takes char2enum, enum2char, enum2str.
/// Constructor for enums that have to_string and from_string.
ParamEnum( const char* name, int width, ParamType type,
ENUM default_value,
const char* help ):
TParamBase<ENUM>( name, width, type, default_value, help ),
char2enum_( nullptr ),
enum2char_( nullptr ),
str2enum_ ( nullptr ),
enum2str_ ( nullptr )
{}

/// Deprecated constructor taking char2enum, enum2char, enum2str.
[[deprecated("Use constructor without char2enum, etc. Remove 2025-02.")]]
ParamEnum( const char* name, int width, ParamType type,
ENUM default_value,
char2enum in_char2enum, enum2char in_enum2char,
Expand All @@ -664,7 +783,8 @@ public:
enum2str_( in_enum2str )
{}

// Takes str2enum, enum2str.
/// Deprecated constructor taking str2enum, enum2str.
[[deprecated("Use constructor without char2enum, etc. Remove 2025-02.")]]
ParamEnum( const char* name, int width, ParamType type,
ENUM default_value,
str2enum in_str2enum, enum2str in_enum2str,
Expand All @@ -681,6 +801,7 @@ public:
virtual void help() const;

protected:
// deprecated: char2enum_, etc.; remove 2025-02.
char2enum char2enum_;
enum2char enum2char_;
str2enum str2enum_;
Expand All @@ -703,10 +824,14 @@ void ParamEnum<ENUM>::parse( const char *str )
str += len;
// Parse word into enum. str2enum_ & char2enum_ throw errors.
ENUM val;
if (str2enum_) {
if constexpr (has_from_string<ENUM>::value) {
val = from_string( buf, ENUM() );
}
else if (str2enum_) {
val = str2enum_( buf );
}
else {
assert( char2enum_ != nullptr );
val = char2enum_( buf[0] );
}
this->push_back( val );
Expand All @@ -726,8 +851,17 @@ template <typename ENUM>
void ParamEnum<ENUM>::print() const
{
if (this->used_ && this->width_ > 0) {
printf( "%*s ", this->width_,
this->enum2str_( this->values_[ this->index_ ] ));
if constexpr (has_to_string<ENUM>::value) {
printf( "%*s ", this->width_,
to_string( this->values_[ this->index_ ] ).c_str() );
}
else if (enum2str_) {
printf( "%*s ", this->width_,
this->enum2str_( this->values_[ this->index_ ] ));
}
else {
throw_error( "no to_string method available" );
}
}
}

Expand All @@ -737,9 +871,19 @@ template <typename ENUM>
void ParamEnum<ENUM>::help() const
{
if (this->type_ == ParamType::Value || this->type_ == ParamType::List) {
printf( " %-16s %s; default %s\n",
this->option_.c_str(), this->help_.c_str(),
this->enum2str_( this->default_value_ ));
if constexpr (has_to_string<ENUM>::value) {
printf( " %-16s %s; default %s\n",
this->option_.c_str(), this->help_.c_str(),
to_string( this->default_value_ ).c_str() );
}
else if (enum2str_) {
printf( " %-16s %s; default %s\n",
this->option_.c_str(), this->help_.c_str(),
this->enum2str_( this->default_value_ ));
}
else {
throw_error( "no to_string method available" );
}
}
}

Expand Down

0 comments on commit 83719b1

Please sign in to comment.