How can I reduce the function template specializations? - c++

I'm trying to write a property validator to be used in UE4, using their reflection system.
I first came up with this:
class FDataValidator
{
public:
FDataValidator( UObject & object, TArray< FText > & validation_errors ) :
Object( object ),
ValidationResult( validation_errors.Num() == 0 ? EDataValidationResult::Valid : EDataValidationResult::Invalid ),
ObjectClass( Object.GetClass() ),
ValidationErrors( validation_errors )
{}
template < class _TYPE_ >
FDataValidator & GreaterThan( const FName property_name, const _TYPE_ value );
EDataValidationResult Result() const
{
return ValidationResult;
}
private:
template < class _PROPERTY_TYPE_, class _VALUE_TYPE_, class _COMPARATOR_TYPE_ >
FDataValidator & CompareProperty( const FName property_name, const _VALUE_TYPE_ value, _COMPARATOR_TYPE_ comparator = _COMPARATOR_TYPE_() )
{
if ( auto * property = GetTypedProperty< _PROPERTY_TYPE_ >( property_name ) )
{
const auto property_value = property->GetPropertyValue_InContainer( &Object );
if ( !comparator( property_value, value ) )
{
AddError( FText::FromString( FString::Printf( TEXT( "%s must be greater than %f" ), *property_name.ToString(), value ) ) );
}
}
return *this;
}
template < class _PROPERTY_TYPE_ >
_PROPERTY_TYPE_ * GetTypedProperty( const FName property_name )
{
if ( auto * property = ObjectClass->FindPropertyByName( property_name ) )
{
if ( auto * typed_property = Cast< _PROPERTY_TYPE_ >( property ) )
{
return typed_property;
}
}
return nullptr;
}
void AddError( FText text )
{
ValidationErrors.Emplace( text );
ValidationResult = EDataValidationResult::Invalid;
}
UObject & Object;
EDataValidationResult ValidationResult;
UClass * ObjectClass;
TArray< FText > & ValidationErrors;
};
template <>
FDataValidator & FDataValidator::GreaterThan< float >( const FName property_name, const float value )
{
return CompareProperty< UFloatProperty, float, std::greater< float > >( property_name, value );
}
template <>
FDataValidator & FDataValidator::GreaterThan< int >( const FName property_name, const int value )
{
return CompareProperty< UIntProperty, int, std::greater< int > >( property_name, value );
}
I only implemented GreaterThan for 2 types, and I'd like to know if I can not refactor the class to avoid so many template specializations.
Would it be possible to have something like that, where I pass for example std::greater< _VALUE_TYPE_ > around and let the system infer what _VALUE_TYPE_ is to call GetTypedProperty with the correct type?
class FDataValidator
{
public:
template < class _VALUE_TYPE_ >
FDataValidator & GreaterThan( const FName property_name, const _VALUE_TYPE_ value )
{
return NumericComparator< std::greater< _VALUE_TYPE_ > >( property_name, value );
}
private:
// How to write this to infer for example that _VALUE_TYPE_ is float when passed std::greater< float > ?
template < typename _COMPARATOR_TYPE_ >
FDataValidator & NumericComparator( FName property_name, _VALUE_TYPE_ value, _COMPARATOR_TYPE_ comparator = _COMPARATOR_TYPE_() );
};
template < class _VALUE_TYPE_, class _COMPARATOR_TYPE_ >
FDataValidator & NumericComparator( FName property_name, _VALUE_TYPE_ value, _COMPARATOR_TYPE_ comparator = _COMPARATOR_TYPE_() )
{
return CompareProperty< UFloatProperty, float, std::greater< float > >( property_name, value );
}
Thanks

UFloatProperty and other numeric types seem to inherit from TProperty_Numeric<T>.
So something like this might be a solution:
template <typename T>
FDataValidator& FDataValidator::GreaterThan<T>(const FName property_name, const T value)
{
return CompareProperty<TProperty_Numeric<T>, T, std::greater<T>>(property_name, value);
}

Related

Performance improvements of a method which check if a string is an ANSI escape sequence?

Let's suppose I have a class with a private method is_escape which check if an input string is and ANSI escape sequence. This method is then used in another public method, into an if/else condition:
#include <string>
enum class ANSI { first, generic };
template <class T_str>
class foo
{
private:
template <typename T>
static constexpr bool is_escape( const T& str, ANSI&& flag ) { /* implementation */ }
public:
template <class T>
void public_method( T str ) // T can be also an int, double etc...
{
if ( is_escape( str ) ) { /* do something */ }
}
};
The implementation of the is_escape method is the following:
#include <string>
#include <type_traits>
template <typename T>
static constexpr bool is_escape( const T& str, ANSI&& flag )
{
if constexpr( std::is_convertible_v <T, std::basic_string_view<T_str>> && ! std::is_same_v<T, std::nullptr_t> )
{
switch( flag )
{
case( ANSI::first ):
{
return ( ! std::basic_string_view<T_str>( str ).rfind( "\033"s, 0 ) ) &&
( std::basic_string_view<T_str>( str ).length() < 7 );
}
case( ANSI::generic ):
{
return ( std::basic_string_view<T_str>( str ).find( "\033"s ) != std::basic_string_view<T_str>::npos );
}
}
}
return false;
}
Is there a better way to write the method in order to improve its performances (in C++17)? Thanks.
At the end, I improved the performances of the function with this signature:
template <typename T>
static constexpr bool is_escape( const T& str, const ANSI& flag )
{
if constexpr( std::is_convertible_v <T, std::basic_string_view<T_str>> && ! std::is_same_v<T, std::nullptr_t> )
{
switch( flag )
{
case( ANSI::first ):
{
return ( std::basic_string_view<T_str>( str ).length() < 7 ) && ( str[0] == '\033' );
}
case( ANSI::generic ):
{
return ( std::basic_string_view<T_str>( str ).find( '\033' ) != std::basic_string_view<T_str>::npos );
}
}
}
return false;
}

Enum convert to string using compile time constants

I'm trying to associate compile time strings to enum values.
Here is my first attempt at the problem:
EnumValue will do the compile time assocation between a string and an enum
template<typename EnumType, int EnumIntValue, const char* EnumStrValue>
class EnumValue
{
public:
static const char* toString()
{
return EnumStrValue;
}
static const int toInt()
{
return EnumIntValue;
}
static EnumType get()
{
return static_cast<EnumType>(EnumIntValue);
}
};
EnumValueHolder will hold the actual values for both string and enum.
I dislike my current design as it still needs to hold a pointer to string. I would prefer a compile time association for this but fail to come up with a more elegant solution
template<typename EnumType>
class EnumValueHolder
{
public:
EnumValueHolder()
{}
EnumValueHolder(const EnumType& value, const char* str)
: value(value), str(str)
{}
bool operator==(const EnumValueHolder<EnumType>& rhs) { return value == rhs.value; }
bool operator==(const EnumType& rhs)const { return value == rhs; }
operator EnumType()const
{
return value;
}
const char* toString()const
{
return str;
}
const int toInt()const
{
return static_cast<int>(value);
}
private:
EnumType value;
char const* str;
};
Marcos to easily refer to enum types and enum value holder construction
#define ENUM_VALUE_TYPE(enumName, enumValue) \
EnumValue<enumName, (int)enumName::enumValue, str_##enumValue>
#define ENUM_VALUE_MAKE(enumName, enumValue) \
EnumValueHolder<enumName> { \
ENUM_VALUE_TYPE(enumName, enumValue)::get(), \
ENUM_VALUE_TYPE(enumName, enumValue)::toString() }
The following are my test cases and usage examples:
const char str_Apple[] = "Apple";
const char str_Orange[] = "Orange";
const char str_Pineapple[] = "Pineapple";
enum class EFruits
{
Apple,
Orange,
Pineapple
};
int main()
{
auto evApple = ENUM_VALUE_MAKE(EFruits, Apple);
std::cout << evApple.toString() << std::endl;
auto evOrange = ENUM_VALUE_MAKE(EFruits, Orange);
std::cout << evOrange.toString() << std::endl;
std::cout << "compare: " << (evApple == evOrange) << std::endl;
evApple = evOrange;
std::cout << evApple.toString() << std::endl;
auto myfruit = ENUM_VALUE_MAKE(EFruits, Pineapple);
std::cout << myfruit.toString() << std::endl;
switch (myfruit)
{
case EFruits::Apple:
std::cout << "Im an apple!" << std::endl;
break;
case EFruits::Orange:
std::cout << "Im an Orange!" << std::endl;
break;
case EFruits::Pineapple:
std::cout << "Im a Pineapple!" << std::endl;
break;
default:break;
}
}
One of the objectives is to remove the global string:
const char str_Apple[] = "Apple";
const char str_Orange[] = "Orange";
const char str_Pineapple[] = "Pineapple";
The other is to create a macro that assoicates an enum with a string
//Some crazy define that makes pairs of enum values and strings as
//compile time constants
#define DEFINE_ENUM_STRING(enumValue)\
enumValue, #enumValue
//Ideally, the macro would be used like this. This should be usable in any
//scope (global, namespace, class)
//with any access specifier (private, protected, public)
enum class EFruits
{
DEFINE_ENUM_STRING(Apple),
DEFINE_ENUM_STRING(Orange),
DEFINE_ENUM_STRING(Pineapple)
};
So there are 2 main questions:
1) Will this current design actually guarantee compile time constants for associating the enum to the string?
2) How can I define a macro to stringify an enum value and declare the value in a enum class using 1 line?
Edit: This should work and compile with msvs2017 on win64 platform using c++ 11.
Thanks.
I think it should work with MSVC2017. It uses C++14 in the constexpr functions but you can split them to single return statement constexprs to be C++11 compatible (however MSVC2017 supports C++14).
EnumConverter stores the char*, the enum and a string hash value for each enum entry. For each enum you must specialize EnumConverter::StrEnumContainer. The enum-string pairs could be generated with a similar macro you specified.
#include <tuple>
#include <array>
#include <stdexcept>
using namespace std;
enum ELogLevel {
Info,
Warn,
Debug,
Error,
Critical
};
static constexpr size_t constexprStringHash( char const* const str ) noexcept
{
return (
( *str != 0 ) ?
( static_cast< size_t >( *str ) + 33 * constexprStringHash( str + 1 ) ) :
5381
);
}
class EnumConverter final
{
public:
EnumConverter() = delete;
EnumConverter( const EnumConverter& ) = delete;
EnumConverter( EnumConverter&& ) = delete;
EnumConverter& operator =( const EnumConverter& ) = delete;
EnumConverter& operator =( EnumConverter&& ) = delete;
template< typename ENUM_T >
static constexpr const char* toStr( const ENUM_T value )
{
const auto& strEnumArray{ StrEnumContainer< ENUM_T >::StrEnumPairs };
const char* result{ nullptr };
for( size_t index{ 0 }; index < strEnumArray.size(); ++index ) {
if( std::get< 1 >( strEnumArray[ index ] ) == value ) {
result = std::get< 0 >( strEnumArray[ index ] );
break;
}
}
return ( ( result == nullptr ) ? throw std::logic_error{ "Enum toStrBase conversion failed" } : result );
}
template< typename ENUM_T >
static constexpr ENUM_T fromStr( const char* const str )
{
const auto& strEnumArray{ StrEnumContainer< ENUM_T >::StrEnumPairs };
const size_t hash{ constexprStringHash( str ) };
const ENUM_T* result{ nullptr };
for( size_t index{ 0 }; index < strEnumArray.size(); ++index ) {
if( std::get< 2 >( strEnumArray[ index ] ) == hash ) {
result = &( std::get< 1 >( strEnumArray[ index ] ) );
}
}
return ( ( result == nullptr ) ? throw std::logic_error{ "Enum toStrBase conversion failed" } : *result );
}
private:
template< typename ENUM_T, size_t LEN >
using ARRAY_T = std::array< std::tuple< const char* const, const ENUM_T, const size_t >, LEN >;
template< typename ENUM_T >
static constexpr std::tuple< const char* const, ENUM_T, size_t > getTuple( const char* const str, const ENUM_T type ) noexcept
{
return std::tuple< const char* const, ENUM_T, size_t >{ str, type, constexprStringHash( str ) };
}
template< typename ENUM_T >
struct StrEnumContainer
{
};
template< typename ENUM_T >
friend struct StrEnumContainer;
};
template<>
struct EnumConverter::StrEnumContainer< ELogLevel >
{
using ENUM_T = ELogLevel;
static constexpr EnumConverter::ARRAY_T< ENUM_T, 5 > StrEnumPairs{ {
{ getTuple( "Info", ENUM_T::Info ) },
{ getTuple( "Warn", ENUM_T::Warn ) },
{ getTuple( "Debug", ENUM_T::Debug ) },
{ getTuple( "Error", ENUM_T::Error ) },
{ getTuple( "Critical", ENUM_T::Critical ) },
} };
};
int main()
{
//static_assert( EnumConverter::fromStr< ELogLevel >( "Info" ) == EnumConverter::fromStr< ELogLevel >( EnumConverter::toStr( Error ) ), "Error" ); // Error
static_assert(
EnumConverter::toStr( Warn )[ 0 ] == 'W' &&
EnumConverter::toStr( Warn )[ 1 ] == 'a' &&
EnumConverter::toStr( Warn )[ 2 ] == 'r' &&
EnumConverter::toStr( Warn )[ 3 ] == 'n',
"Error"
);
static_assert( EnumConverter::fromStr< ELogLevel >( "Info" ) == EnumConverter::fromStr< ELogLevel >( EnumConverter::toStr( Info ) ), "Error" );
}

No instance of overloaded function when passing single or vector of objects

I'm trying to pass an object and a vector of class objects or two objects to two overloaded template functions named checkPosition.
No matter what I try, changing my function to passing by value / reference / pointer / const / not const, it gives me the error;
Error: no instance of overloaded function "Room::checkPosition" matches the argument list
argument types are: (const Player, const std::vector< Monster, std::allocator< Monster > > )
object type is: const Room.
From Room.h:
class Room
{
public:
Player player;
std::vector<Monster> monster;
std::vector<Wall> wall;
std::vector<Exit> exit;
template<class T1> void xSet( T1 &object, const int &input );
template<class T2> void ySet( T2 &object, const int &input );
template<class T3> int x( T3 &object );
template<class T4> int y( T4 &object );
template<class T5> bool checkPosition( const T5 &object1, const T5 &object2 );
template<class T6> bool checkPosition( const T6 &object1, const std::vector<T6> &object2 );
void objectIconSet( );
void lengthSet( const int &input );
void widthSet( const int &input );
int length( );
int width( );
void staticDataMap( );
void completeDataMap( );
char staticDataMap( int x, int y );
char completeDataMap( int x, int y );
private:
int _length;
int _width;
std::vector< std::vector<char> > _staticDataMap;
std::vector< std::vector<char> > _completeDataMap;
};
From Room.cpp
template<class T5> bool Room::checkPosition( const T5 &object1, const T5 &object2 )
{
if( object1.position.x == object2.position.x &&
object1.position.y == object2.position.y )
{
return true
}
return false;
}
template<class T6> bool Room::checkPosition( const T6 &object1, const std::vector<T6> &object2 )
{
for( int i = 0; i < object2.size( ); i++ )
{
if( object1.position.x == object2[i].position.x &&
object1.position.y == object2[i].position.y )
{
return true
}
}
return false;
}
Examples of function use in main.cpp:
bool checkWinCondition( const Room &room )
{
if( room.checkPosition( room.player, room.exit ) == true )
{
std::cout << "\nYou win!";
return true;
}
return false;
}
bool checkLoseCondition( const Room &room )
{
if( room.checkPosition( room.player, room.monster ) == true )
{
std::cout << "\nYou lose!";
return true;
}
return false;
}
void SetRoomOuterWalls( Room &room )
{
Position tempPosition;
Wall tempWall;
for( int y = 0; y < room.length( ); y++ )
{
for( int x = 0; x < room.width( ); x++ )
{
tempPosition.x = x;
tempPosition.y = y;
if( room.checkPosition( tempPosition, room.exit ) == true )
{
continue;
}
else if( x == 0 || x == room.width( ) - 1 ||
y == 0 || y == room.length( ) - 1 )
{
room.wall.push_back( tempWall );
room.xSet( room.wall, x );
room.xSet( room.wall, y );
}
}
}
}
You are calling your function with two different parameters but your function is templated to one type
template<class T6> bool checkPosition( const T6 &object1, const std::vector<T6> &object2 );
Means that that you want an object and a vector of objects of the same type. You are passing into the function a player and a vector<monster> which does not match. What you can do is change your template to:
template<class T, class Y> bool checkPosition( const T &object1, const std::vector<Y> &object2 );
Which will allow you to take some object and a vector of the same type or another type.

Search in a vector of structs

I have a vector full with structs look like this
struct person_t
{
string name;
string id;
struct location_t location;
};
vector <person_t> myvector;
and i have read in the items in myvector
but now i need to know how to search for specific item in vector and count how many of that item it is in the vector.
unsigned int count = std::count_if( myvector.begin(), myvector.begin(),
[]( const person_t & p ) { return p.name == "Bill Gates"; }
);
or without c++11
struct EqualByName {
EqualByName( const std::string & name ) : m_name( name ) {}
bool operator()( const person_t & p ) const { return p.name == m_name; }
private:
std::string m_name;
};
unsigned int count = std::count_if( myvector.begin(), myvector.begin(),
EqualByName( "Bill Gates" )
);
or ugly looking but for all occasions ))
template< class T, class FieldType, FieldType T::*FieldPtr >
struct EqualBy
{
EqualBy( const FieldType & value ) : m_fieldValue( value ) {}
bool operator()( const T & r ) const {
return m_fieldValue == r.*FieldPtr;
}
bool operator()( const T * p ) const {
return m_fieldValue == p->*FieldPtr;
}
private:
const FieldType m_fieldValue;
};
// usage:
typedef EqualBy< person_t, std::string, & person_t::name > EqualByName;
typedef EqualBy< person_t, std::string, & person_t::id > EqualById;
unsigned int namesCount = std::count_if( myvector.begin(), myvector.end(),
EqualByName( "Bill Gates" )
);
unsigned int idsCount = std::count_if( myvector.begin(), myvector.end(),
EqualById( "123456" )
);

Unable to insert more than 256 nodes into a custom tree

I've been stuck on this for quite some time now and have even tested the issue between a 64-bit version of gcc on Ubuntu as welll as a 32-bit gcc on Windows (MinGW).
Any time I insert more than 256 nodes into a binary-tree(?), it stops counting the number of nodes. I can still access all of my data. I have a feeling that it has something to do with the way I have my structure setup, by using chars to acquire each bit of each byte, but I have no idea how to fix it.
In this header, I have a structure and some functions setup which allows me to acquire an individual bit of an object.
This is the actual tree implementation. In order to find where to store each object, the tree iterates through each byte of a key, then iterates again through each bit of those bytes. The "iterate" function is what is giving me the most difficulty though; I have no idea why, but once 256 nodes become filled with data, my structure stops counting further, then begins to replace all previous data. I believe this has something to do with the fact that a single char can only hold 0-256, but I can't see where this would be an issue. Since the location of each node is determined by the individual bits of the key, it's hard to determine why only 256 items can be placed into the tree.
The URL to my test program is at the bottom of the post. SO won't let me post more than 2 at the moment. I would like to get this done soon, so any help would be greatly appreciated.
Edit:
Just to make things easier, this is the structure that gives me the individual bit of a byte, as well as a helper function:
struct bitMask {
char b1 : 1;
char b2 : 1;
char b3 : 1;
char b4 : 1;
char b5 : 1;
char b6 : 1;
char b7 : 1;
char b8 : 1;
char operator[] ( unsigned i ) const {
switch( i ) {
case 0 : return b1;
case 1 : return b2;
case 2 : return b3;
case 3 : return b4;
case 4 : return b5;
case 5 : return b6;
case 6 : return b7;
case 7 : return b8;
}
return 0; // Avoiding a compiler error
}
};
/******************************************************************************
* Functions shared between tree-type objects
******************************************************************************/
namespace treeShared {
// Function to retrieve the next set of bits at the pointer "key"
template <typename key_t>
inline const bitMask* getKeyByte( const key_t* key, unsigned iter );
/* template specializations */
template <>
inline const bitMask* getKeyByte( const char*, unsigned );
template <>
inline const bitMask* getKeyByte( const wchar_t*, unsigned );
template <>
inline const bitMask* getKeyByte( const char16_t*, unsigned );
template <>
inline const bitMask* getKeyByte( const char32_t*, unsigned );
} // end treeShared namespace
/*
* Tree Bit Mask Function
*/
template <typename key_t>
inline const bitMask* treeShared::getKeyByte( const key_t* k, unsigned iter ) {
return (iter < sizeof( key_t ))
? reinterpret_cast< const bitMask* >( k+iter )
: nullptr;
}
/*
* Tree Bit Mask Specializations
*/
template <>
inline const bitMask* treeShared::getKeyByte( const char* str, unsigned iter ) {
return (str[ iter ] != '\0')
? reinterpret_cast< const bitMask* >( str+iter )
: nullptr;
}
template <>
inline const bitMask* treeShared::getKeyByte( const wchar_t* str, unsigned iter ) {
return (str[ iter ] != '\0')
? reinterpret_cast< const bitMask* >( str+iter )
: nullptr;
}
template <>
inline const bitMask* treeShared::getKeyByte( const char16_t* str, unsigned iter ) {
return (str[ iter ] != '\0')
? reinterpret_cast< const bitMask* >( str+iter )
: nullptr;
}
template <>
inline const bitMask* treeShared::getKeyByte( const char32_t* str, unsigned iter ) {
return (str[ iter ] != '\0')
? reinterpret_cast< const bitMask* >( str+iter )
: nullptr;
}
And here is the tree class:
template <typename data_t>
struct bTreeNode {
data_t* data = nullptr;
bTreeNode* subNodes = nullptr;
~bTreeNode() {
delete data;
delete [] subNodes;
data = nullptr;
subNodes = nullptr;
}
};
/******************************************************************************
* Binary-Tree Structure Setup
******************************************************************************/
template <typename key_t, typename data_t>
class bTree {
enum node_dir : unsigned {
BNODE_LEFT = 0,
BNODE_RIGHT = 1,
BNODE_MAX
};
protected:
bTreeNode<data_t> head;
unsigned numNodes = 0;
private:
bTreeNode<data_t>* iterate( const key_t* k, bool createNodes );
public:
~bTree() {}
// STL-Map behavior
data_t& operator [] ( const key_t& k );
void push ( const key_t& k, const data_t& d );
void pop ( const key_t& k );
bool hasData ( const key_t& k );
const data_t* getData ( const key_t& k );
unsigned size () const { return numNodes; }
void clear ();
};
/*
* Binary-Tree -- Element iteration
*/
template <typename key_t, typename data_t>
bTreeNode<data_t>* bTree<key_t, data_t>::iterate( const key_t* k, bool createNodes ) {
node_dir dir;
unsigned bytePos = 0;
bTreeNode<data_t>* bNodeIter = &head;
const bitMask* byteIter = nullptr;
while ( byteIter = treeShared::getKeyByte< key_t >( k, bytePos++ ) ) {
for ( int currBit = 0; currBit < HL_BITS_PER_BYTE; ++currBit ) {
// compare the bits of each byte in k
dir = byteIter->operator []( currBit ) ? BNODE_LEFT : BNODE_RIGHT;
// check to see if a new bTreeNode needs to be made
if ( !bNodeIter->subNodes ) {
if ( createNodes ) {
// create and initialize the upcoming sub bTreeNode
bNodeIter->subNodes = new bTreeNode<data_t>[ BNODE_MAX ];
}
else {
return nullptr;
}
}
// move to the next bTreeNode
bNodeIter = &(bNodeIter->subNodes[ dir ]);
}
}
return bNodeIter;
}
/*
* Binary-Tree -- Destructor
*/
template <typename key_t, typename data_t>
void bTree<key_t, data_t>::clear() {
delete head.data;
delete [] head.subNodes;
head.data = nullptr;
head.subNodes = nullptr;
numNodes = 0;
}
/*
* Binary-Tree -- Array Subscript operators
*/
template <typename key_t, typename data_t>
data_t& bTree<key_t, data_t>::operator []( const key_t& k ) {
bTreeNode<data_t>* iter = iterate( &k, true );
if ( !iter->data ) {
iter->data = new data_t();
++numNodes;
}
return *iter->data;
}
/*
* Binary-Tree -- Push
* Push a data element to the tree using a key
*/
template <typename key_t, typename data_t>
void bTree<key_t, data_t>::push( const key_t& k, const data_t& d ) {
bTreeNode<data_t>* iter = iterate( &k, true );
if ( !iter->data ) {
iter->data = new data_t( d );
++numNodes;
}
else {
*iter->data = d;
}
}
/*
* Binary-Tree -- Pop
* Remove whichever element lies at the key
*/
template <typename key_t, typename data_t>
void bTree<key_t, data_t>::pop( const key_t& k ) {
bTreeNode<data_t>* iter = iterate( &k, false );
if ( !iter || !iter->data )
return;
delete iter->data;
iter->data = nullptr;
--numNodes;
}
/*
* Binary-Tree -- Has Data
* Return true if there is a data element at the key
*/
template <typename key_t, typename data_t>
bool bTree<key_t, data_t>::hasData( const key_t& k ) {
bTreeNode<data_t>* iter = iterate( &k, false );
return iter && ( iter->data != nullptr );
}
/*
* Binary-Tree -- Push
* Return a pointer to the data that lies at a key
* Returns a nullptr if no data exists
*/
template <typename key_t, typename data_t>
const data_t* bTree<key_t, data_t>::getData( const key_t& k ) {
bTreeNode<data_t>* iter = iterate( &k, false );
if ( !iter )
return nullptr;
return iter->data;
}
pastebin.com/8MZ0TMpj
template <typename key_t>
inline const bitMask* treeShared::getKeyByte( const key_t* k, unsigned iter ) {
return (iter < sizeof( key_t ))
? reinterpret_cast< const bitMask* >( k+iter )
: nullptr;
}
This doesn't do what you seem to think it does. (k+iter) doesn't retrieve the iter'th byte of k, but the iter'th element of the key_t[] array pointed to by k. In other words, k+iter advances the pointer by iter*sizeof(key_t) bytes, not by iter bytes.
Formally, this code exhibits undefined behavior, by overrunning array bounds. Practically speaking, your program uses just a single byte of the key, and then sizeof(key_t)-1 random bytes that just happen to sit in memory above that key. That's why you are effectively limited to 8 bits of state.
In addition, your reinterpret_cast also exhibits undefined behavior, formally speaking. The only legal use for a pointer obtained with reinterpret_cast is to reinterpret_cast it right back to the original type. This is not the immediate cause of your problem though.