mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-25 22:50:25 -05:00
AppEnum: Type-erase the mapping machinery into a non-template base
Move the loops, lookups, and QString machinery out of AppEnum<T>::EnumMapper into a new non-template AppEnumMapperBase defined in cafAppEnum.cpp. Each member is now compiled once for the whole program rather than once per enum type, cutting build time and binary size for a class template instantiated across hundreds of enums. AppEnum<T> becomes a thin static_cast shim around the base methods. Enum values are stored as int in the mapping; a static_assert rejects enum types whose underlying type is wider than int. The widest underlying type currently in use in ResInsight is short, so all current instantiations are unaffected. Use std::optional<int> for the mapper default value, and assert against an empty mapping in defaultValue() (the prior code returned m_mapping[0] unconditionally, which is undefined behavior on empty). Document why mapper() uses an explicit init flag rather than std::call_once or a magic-static lambda: setUp() re-enters mapper() via addItem, so anything that locks on first init would deadlock. First-access must therefore be single-threaded, as already exercised by BaseTest.CreateObjectInMultipleThreads. Add AppEnumTest.MappingRoundTrip covering text <-> enum, index <-> enum, isValid, and the failure paths of setFromText / setFromIndex (resets to default, returns false).
This commit is contained in:
1 parent
e8db66a377
commit
7fcd192b71
4 files changed
+331
-206
No files matched your search
@@ -17,6 +17,7 @@ qt_standard_project_setup()
|
||||
set(PROJECT_FILES
|
||||
cafAssert.h
|
||||
cafAppEnum.h
|
||||
cafAppEnum.cpp
|
||||
cafClassTypeName.h
|
||||
cafPdmBase.h
|
||||
cafPdmChildArrayField.h
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
//##################################################################################################
|
||||
//
|
||||
// Custom Visualization Core library
|
||||
// Copyright (C) 2011-2013 Ceetron AS
|
||||
//
|
||||
// This library may be used under the terms of either the GNU General Public License or
|
||||
// the GNU Lesser General Public License as follows:
|
||||
//
|
||||
// GNU General Public License Usage
|
||||
// This library 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.
|
||||
//
|
||||
// This library 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 at <<http://www.gnu.org/licenses/gpl.html>>
|
||||
// for more details.
|
||||
//
|
||||
// GNU Lesser General Public License Usage
|
||||
// This library is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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 Lesser General Public License at <<http://www.gnu.org/licenses/lgpl-2.1.html>>
|
||||
// for more details.
|
||||
//
|
||||
//##################################################################################################
|
||||
|
||||
// AppEnumMapperBase holds the type-erased mapping (int <-> text/uiText/aliases) shared by every
|
||||
// AppEnum<T> instantiation. Defining its members in this translation unit instead of inline in the
|
||||
// class template means each lookup and QString helper is compiled once for the whole program rather
|
||||
// than once per enum type, which keeps build times and binary size in check.
|
||||
|
||||
#include "cafAppEnum.h"
|
||||
|
||||
#include "cafAssert.h"
|
||||
|
||||
namespace caf
|
||||
{
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool AppEnumMapperBase::EnumData::isMatching( const QString& text ) const
|
||||
{
|
||||
return text == m_text || m_aliases.contains( text );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void AppEnumMapperBase::addItem( int enumVal, const QString& text, QString uiText, const QStringList& aliases )
|
||||
{
|
||||
// Make sure the alias text is unique for enum
|
||||
for ( const auto& alias : aliases )
|
||||
{
|
||||
for ( const auto& enumData : m_mapping )
|
||||
{
|
||||
CAF_ASSERT( !enumData.isMatching( alias ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the text is trimmed, as this text is streamed to XML and will be trimmed when read back
|
||||
// from XML text https://github.com/OPM/ResInsight/issues/7829
|
||||
m_mapping.push_back( { enumVal, text.trimmed(), uiText, aliases } );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void AppEnumMapperBase::setDefault( int defaultEnumValue )
|
||||
{
|
||||
m_defaultValue = defaultEnumValue;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
int AppEnumMapperBase::defaultValue() const
|
||||
{
|
||||
if ( m_defaultValue.has_value() ) return *m_defaultValue;
|
||||
CAF_ASSERT( !m_mapping.empty() );
|
||||
return m_mapping[0].m_enumVal;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool AppEnumMapperBase::isValid( const QString& text ) const
|
||||
{
|
||||
for ( size_t idx = 0; idx < m_mapping.size(); ++idx )
|
||||
{
|
||||
if ( text == m_mapping[idx].m_text ) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t AppEnumMapperBase::size() const
|
||||
{
|
||||
return m_mapping.size();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool AppEnumMapperBase::enumVal( int& value, const QString& text ) const
|
||||
{
|
||||
value = defaultValue();
|
||||
|
||||
QString trimmedText = text.trimmed();
|
||||
|
||||
for ( size_t idx = 0; idx < m_mapping.size(); ++idx )
|
||||
{
|
||||
if ( m_mapping[idx].isMatching( trimmedText ) )
|
||||
{
|
||||
value = m_mapping[idx].m_enumVal;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool AppEnumMapperBase::enumVal( int& value, size_t index ) const
|
||||
{
|
||||
value = defaultValue();
|
||||
if ( index < m_mapping.size() )
|
||||
{
|
||||
value = m_mapping[index].m_enumVal;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t AppEnumMapperBase::index( int enumValue ) const
|
||||
{
|
||||
size_t idx;
|
||||
for ( idx = 0; idx < m_mapping.size(); ++idx )
|
||||
{
|
||||
if ( enumValue == m_mapping[idx].m_enumVal ) return idx;
|
||||
}
|
||||
return idx; // returns size() if not found, matching prior behavior
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QString AppEnumMapperBase::uiText( int value ) const
|
||||
{
|
||||
for ( size_t idx = 0; idx < m_mapping.size(); ++idx )
|
||||
{
|
||||
if ( value == m_mapping[idx].m_enumVal ) return m_mapping[idx].m_uiText;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QStringList AppEnumMapperBase::uiTexts() const
|
||||
{
|
||||
QStringList uiTextList;
|
||||
for ( size_t idx = 0; idx < m_mapping.size(); ++idx )
|
||||
{
|
||||
uiTextList.append( m_mapping[idx].m_uiText );
|
||||
}
|
||||
return uiTextList;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
QString AppEnumMapperBase::text( int value ) const
|
||||
{
|
||||
for ( size_t idx = 0; idx < m_mapping.size(); ++idx )
|
||||
{
|
||||
if ( value == m_mapping[idx].m_enumVal ) return m_mapping[idx].m_text;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace caf
|
||||
@@ -36,13 +36,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cafAssert.h"
|
||||
#include "cafPdmFieldHandle.h"
|
||||
#include "cafPdmFieldTraits.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace caf
|
||||
@@ -119,11 +121,50 @@ public:
|
||||
virtual void setTextForSerialization( const QString& text ) = 0;
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
/// Non-template backing store for AppEnum<T>. Holds the (text, uiText, aliases) mapping keyed by
|
||||
/// the enum value cast to int. By keeping all loops, lookups, and QString machinery in this
|
||||
/// non-template class, every member is compiled exactly once instead of once per enum type.
|
||||
/// AppEnum<T> reduces to a thin static_cast shim around the relevant methods.
|
||||
//==================================================================================================
|
||||
class AppEnumMapperBase
|
||||
{
|
||||
public:
|
||||
void addItem( int enumVal, const QString& text, QString uiText, const QStringList& aliases );
|
||||
void setDefault( int defaultEnumValue );
|
||||
int defaultValue() const;
|
||||
bool isValid( const QString& text ) const;
|
||||
size_t size() const;
|
||||
bool enumVal( int& value, const QString& text ) const;
|
||||
bool enumVal( int& value, size_t index ) const;
|
||||
size_t index( int enumValue ) const;
|
||||
QString uiText( int value ) const;
|
||||
QStringList uiTexts() const;
|
||||
QString text( int value ) const;
|
||||
|
||||
private:
|
||||
struct EnumData
|
||||
{
|
||||
int m_enumVal;
|
||||
QString m_text;
|
||||
QString m_uiText;
|
||||
QStringList m_aliases;
|
||||
bool isMatching( const QString& text ) const;
|
||||
};
|
||||
|
||||
std::vector<EnumData> m_mapping;
|
||||
std::optional<int> m_defaultValue;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class AppEnum : public AppEnumInterface
|
||||
{
|
||||
static_assert( std::is_enum_v<T>, "AppEnum<T> requires T to be an enum type." );
|
||||
static_assert( sizeof( std::underlying_type_t<T> ) <= sizeof( int ),
|
||||
"AppEnum stores enum values as int; T's underlying type is wider than int." );
|
||||
|
||||
public:
|
||||
AppEnum() { m_value = EnumMapper::instance()->defaultValue(); }
|
||||
AppEnum() { m_value = static_cast<T>( mapper().defaultValue() ); }
|
||||
AppEnum( T value )
|
||||
: m_value( value )
|
||||
{
|
||||
@@ -160,44 +201,56 @@ public:
|
||||
operator T() const { return m_value; }
|
||||
|
||||
T value() const { return m_value; }
|
||||
size_t index() const { return EnumMapper::instance()->index( m_value ); }
|
||||
QString text() const { return EnumMapper::instance()->text( m_value ); }
|
||||
QString uiText() const { return EnumMapper::instance()->uiText( m_value ); }
|
||||
size_t index() const { return mapper().index( static_cast<int>( m_value ) ); }
|
||||
QString text() const { return mapper().text( static_cast<int>( m_value ) ); }
|
||||
QString uiText() const { return mapper().uiText( static_cast<int>( m_value ) ); }
|
||||
|
||||
AppEnum& operator=( T value )
|
||||
{
|
||||
m_value = value;
|
||||
return *this;
|
||||
}
|
||||
bool setFromText( const QString& text ) { return EnumMapper::instance()->enumVal( m_value, text ); }
|
||||
bool setFromIndex( size_t index ) { return EnumMapper::instance()->enumVal( m_value, index ); }
|
||||
bool setFromText( const QString& text )
|
||||
{
|
||||
int v = 0;
|
||||
bool ok = mapper().enumVal( v, text );
|
||||
m_value = static_cast<T>( v );
|
||||
return ok;
|
||||
}
|
||||
bool setFromIndex( size_t index )
|
||||
{
|
||||
int v = 0;
|
||||
bool ok = mapper().enumVal( v, index );
|
||||
m_value = static_cast<T>( v );
|
||||
return ok;
|
||||
}
|
||||
|
||||
QString textForSerialization() const override { return text(); }
|
||||
void setTextForSerialization( const QString& text ) override { setFromText( text ); }
|
||||
|
||||
// Static interface to access the properties of the enum definition
|
||||
|
||||
static bool isValid( const QString& text ) { return EnumMapper::instance()->isValid( text ); }
|
||||
static bool isValid( size_t index ) { return index < EnumMapper::instance()->size(); }
|
||||
static size_t size() { return EnumMapper::instance()->size(); }
|
||||
static bool isValid( const QString& text ) { return mapper().isValid( text ); }
|
||||
static bool isValid( size_t index ) { return index < mapper().size(); }
|
||||
static size_t size() { return mapper().size(); }
|
||||
|
||||
static QStringList uiTexts() { return EnumMapper::instance()->uiTexts(); }
|
||||
static QStringList uiTexts() { return mapper().uiTexts(); }
|
||||
static T fromIndex( size_t idx )
|
||||
{
|
||||
T val;
|
||||
EnumMapper::instance()->enumVal( val, idx );
|
||||
return val;
|
||||
int v = 0;
|
||||
mapper().enumVal( v, idx );
|
||||
return static_cast<T>( v );
|
||||
}
|
||||
static T fromText( const QString& text )
|
||||
{
|
||||
T val;
|
||||
EnumMapper::instance()->enumVal( val, text );
|
||||
return val;
|
||||
int v = 0;
|
||||
mapper().enumVal( v, text );
|
||||
return static_cast<T>( v );
|
||||
}
|
||||
static size_t index( T enumValue ) { return EnumMapper::instance()->index( enumValue ); }
|
||||
static QString text( T enumValue ) { return EnumMapper::instance()->text( enumValue ); }
|
||||
static size_t index( T enumValue ) { return mapper().index( static_cast<int>( enumValue ) ); }
|
||||
static QString text( T enumValue ) { return mapper().text( static_cast<int>( enumValue ) ); }
|
||||
static QString textFromIndex( size_t idx ) { return text( fromIndex( idx ) ); }
|
||||
static QString uiText( T enumValue ) { return EnumMapper::instance()->uiText( enumValue ); }
|
||||
static QString uiText( T enumValue ) { return mapper().uiText( static_cast<int>( enumValue ) ); }
|
||||
static QString uiTextFromIndex( size_t idx ) { return uiText( fromIndex( idx ) ); }
|
||||
|
||||
private:
|
||||
@@ -209,203 +262,39 @@ private:
|
||||
static void setUp();
|
||||
static void addItem( T enumVal, const QString& text, const QString& uiText, const QStringList& aliases = {} )
|
||||
{
|
||||
EnumMapper::instance()->addItem( enumVal, text, uiText, aliases );
|
||||
mapper().addItem( static_cast<int>( enumVal ), text, uiText, aliases );
|
||||
}
|
||||
|
||||
static void setDefault( T defaultEnumValue ) { EnumMapper::instance()->setDefault( defaultEnumValue ); }
|
||||
static void setDefault( T defaultEnumValue ) { mapper().setDefault( static_cast<int>( defaultEnumValue ) ); }
|
||||
|
||||
// Per-T storage for the mapper. The base class methods are non-template so they're compiled
|
||||
// once total; only this accessor and the trivial static_cast shims above are instantiated per T.
|
||||
//
|
||||
// The explicit isInitialized flag (rather than e.g. std::call_once or a magic-static lambda) is
|
||||
// deliberate: setUp() invokes addItem(), which calls mapper() recursively. Setting the flag
|
||||
// before invoking setUp() lets the re-entrant calls return the storedInstance instead of
|
||||
// deadlocking. First-access must therefore happen single-threaded; see
|
||||
// CreateObjectInMultipleThreads in cafPdmBasicTest.cpp.
|
||||
static AppEnumMapperBase& mapper()
|
||||
{
|
||||
static AppEnumMapperBase storedInstance;
|
||||
static bool isInitialized = false;
|
||||
if ( !isInitialized )
|
||||
{
|
||||
isInitialized = true;
|
||||
AppEnum<T>::setUp();
|
||||
}
|
||||
return storedInstance;
|
||||
}
|
||||
|
||||
T m_value;
|
||||
|
||||
static std::map<QString, std::vector<T>> m_enumSubset; // Key format: "ownerClass::fieldKeyword"
|
||||
|
||||
//==================================================================================================
|
||||
/// A private class to handle the instance of the mapping vector.
|
||||
/// all access methods could have been placed directly in the \class AppEnum class,
|
||||
/// but AppEnum implementation gets nicer this way.
|
||||
/// The real core of this class is the vector map member and the static instance method
|
||||
//==================================================================================================
|
||||
|
||||
class EnumMapper
|
||||
{
|
||||
private:
|
||||
class EnumData
|
||||
{
|
||||
public:
|
||||
EnumData( T enumVal, const QString& text, const QString& uiText, const QStringList& aliases )
|
||||
: m_enumVal( enumVal )
|
||||
, m_text( text )
|
||||
, m_uiText( uiText )
|
||||
, m_aliases( aliases )
|
||||
{
|
||||
}
|
||||
|
||||
bool isMatching( const QString& text ) const { return ( text == m_text || m_aliases.contains( text ) ); }
|
||||
|
||||
T m_enumVal;
|
||||
QString m_text;
|
||||
QString m_uiText;
|
||||
QStringList m_aliases;
|
||||
};
|
||||
|
||||
public:
|
||||
void addItem( T enumVal, const QString& text, QString uiText, const QStringList& aliases )
|
||||
{
|
||||
// Make sure the alias text is unique for enum
|
||||
for ( const auto& alias : aliases )
|
||||
{
|
||||
for ( const auto& enumData : instance()->m_mapping )
|
||||
{
|
||||
CAF_ASSERT( !enumData.isMatching( alias ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the text is trimmed, as this text is streamed to XML and will be trimmed when read back
|
||||
// from XML text https://github.com/OPM/ResInsight/issues/7829
|
||||
instance()->m_mapping.push_back( EnumData( enumVal, text.trimmed(), uiText, aliases ) );
|
||||
}
|
||||
|
||||
static EnumMapper* instance()
|
||||
{
|
||||
static EnumMapper storedInstance;
|
||||
static bool isInitialized = false;
|
||||
if ( !isInitialized )
|
||||
{
|
||||
isInitialized = true;
|
||||
AppEnum<T>::setUp();
|
||||
}
|
||||
return &storedInstance;
|
||||
}
|
||||
|
||||
void setDefault( T defaultEnumValue )
|
||||
{
|
||||
m_defaultValue = defaultEnumValue;
|
||||
m_defaultValueIsSet = true;
|
||||
}
|
||||
|
||||
T defaultValue() const
|
||||
{
|
||||
if ( m_defaultValueIsSet )
|
||||
{
|
||||
return m_defaultValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// CAF_ASSERT(m_mapping.size());
|
||||
return m_mapping[0].m_enumVal;
|
||||
}
|
||||
}
|
||||
|
||||
bool isValid( const QString& text ) const
|
||||
{
|
||||
size_t idx;
|
||||
for ( idx = 0; idx < m_mapping.size(); ++idx )
|
||||
{
|
||||
if ( text == m_mapping[idx].m_text ) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t size() const { return m_mapping.size(); }
|
||||
|
||||
bool enumVal( T& value, const QString& text ) const
|
||||
{
|
||||
value = defaultValue();
|
||||
|
||||
QString trimmedText = text.trimmed();
|
||||
|
||||
for ( size_t idx = 0; idx < m_mapping.size(); ++idx )
|
||||
{
|
||||
// Make sure the text parsed from a text stream is trimmed
|
||||
// https://github.com/OPM/ResInsight/issues/7829
|
||||
if ( m_mapping[idx].isMatching( trimmedText ) )
|
||||
{
|
||||
value = m_mapping[idx].m_enumVal;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool enumVal( T& value, size_t index ) const
|
||||
{
|
||||
value = defaultValue();
|
||||
if ( index < m_mapping.size() )
|
||||
{
|
||||
value = m_mapping[index].m_enumVal;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t index( T enumValue ) const
|
||||
{
|
||||
size_t idx;
|
||||
for ( idx = 0; idx < m_mapping.size(); ++idx )
|
||||
{
|
||||
if ( enumValue == m_mapping[idx].m_enumVal ) return idx;
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
QString uiText( T value ) const
|
||||
{
|
||||
size_t idx;
|
||||
for ( idx = 0; idx < m_mapping.size(); ++idx )
|
||||
{
|
||||
if ( value == m_mapping[idx].m_enumVal ) return m_mapping[idx].m_uiText;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
QStringList uiTexts() const
|
||||
{
|
||||
QStringList uiTextList;
|
||||
size_t idx;
|
||||
for ( idx = 0; idx < m_mapping.size(); ++idx )
|
||||
{
|
||||
uiTextList.append( m_mapping[idx].m_uiText );
|
||||
}
|
||||
return uiTextList;
|
||||
}
|
||||
|
||||
QString text( T value ) const
|
||||
{
|
||||
size_t idx;
|
||||
for ( idx = 0; idx < m_mapping.size(); ++idx )
|
||||
{
|
||||
if ( value == m_mapping[idx].m_enumVal ) return m_mapping[idx].m_text;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private:
|
||||
EnumMapper()
|
||||
: m_defaultValue( T() )
|
||||
, m_defaultValueIsSet( false )
|
||||
{
|
||||
}
|
||||
|
||||
friend class AppEnum<T>;
|
||||
|
||||
std::vector<EnumData> m_mapping;
|
||||
T m_defaultValue;
|
||||
bool m_defaultValueIsSet;
|
||||
};
|
||||
};
|
||||
|
||||
template <class T>
|
||||
std::map<QString, std::vector<T>> AppEnum<T>::m_enumSubset;
|
||||
|
||||
} // namespace caf
|
||||
|
||||
#include "cafPdmFieldTraits.h"
|
||||
|
||||
namespace caf
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
QVariant pdmToVariant( const AppEnum<T>& value )
|
||||
{
|
||||
|
||||
@@ -70,6 +70,43 @@ public:
|
||||
caf::PdmDataValueField<caf::AppEnum<TestEnumType>> m_enumField;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Round-trip exercise of the type-erased mapping in AppEnumMapperBase: text <-> enum, index
|
||||
/// <-> enum, and out-of-range / unknown lookups.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( AppEnumTest, MappingRoundTrip )
|
||||
{
|
||||
using AE = caf::AppEnum<TestEnumType>;
|
||||
|
||||
EXPECT_EQ( 5u, AE::size() );
|
||||
|
||||
EXPECT_EQ( QString( "VALUE_C" ), AE::text( TestEnumType::VALUE_C ) );
|
||||
EXPECT_EQ( QString( "Value C" ), AE::uiText( TestEnumType::VALUE_C ) );
|
||||
EXPECT_EQ( 2u, AE::index( TestEnumType::VALUE_C ) );
|
||||
|
||||
EXPECT_EQ( TestEnumType::VALUE_D, AE::fromText( "VALUE_D" ) );
|
||||
EXPECT_EQ( TestEnumType::VALUE_D, AE::fromIndex( 3 ) );
|
||||
|
||||
EXPECT_TRUE( AE::isValid( QString( "VALUE_A" ) ) );
|
||||
EXPECT_FALSE( AE::isValid( QString( "NOT_A_VALUE" ) ) );
|
||||
EXPECT_TRUE( AE::isValid( size_t{ 0 } ) );
|
||||
EXPECT_FALSE( AE::isValid( size_t{ 99 } ) );
|
||||
|
||||
AE e;
|
||||
EXPECT_EQ( TestEnumType::VALUE_A, e.value() ); // Default is VALUE_A
|
||||
EXPECT_TRUE( e.setFromText( "VALUE_E" ) );
|
||||
EXPECT_EQ( TestEnumType::VALUE_E, e.value() );
|
||||
EXPECT_TRUE( e.setFromIndex( 1 ) );
|
||||
EXPECT_EQ( TestEnumType::VALUE_B, e.value() );
|
||||
|
||||
// Unknown text and out-of-range index return false and reset to default
|
||||
EXPECT_FALSE( e.setFromText( "DOES_NOT_EXIST" ) );
|
||||
EXPECT_EQ( TestEnumType::VALUE_A, e.value() );
|
||||
e = TestEnumType::VALUE_C;
|
||||
EXPECT_FALSE( e.setFromIndex( 99 ) );
|
||||
EXPECT_EQ( TestEnumType::VALUE_A, e.value() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Test that two different objects can have the same field keyword with different enum subsets
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
Reference in new issue
Block a user