Skip to content
Snippets Groups Projects
Commit c932dbe7 authored by Jonathan Hanks's avatar Jonathan Hanks
Browse files

Adding a basic vector with a fixed backing store for use by the epics sequencer.

Also added some more checks for fixed_size_string to ensure it can meet the requirements to be in the vector.
parent d8a93c17
No related branches found
No related tags found
2 merge requests!439RCG 5.0 release fro deb 10,!343convert the sequencer to C++
add_executable(test_sequencer_unit_tests
test/test_main.cc
test/test_fixed_size_string.cc
test/test_fixed_size_vector.cc
)
target_include_directories(test_sequencer_unit_tests PUBLIC
${CMAKE_CURRENT_SOURCE_DIR})
......
//
// Created by jonathan.hanks on 11/13/20.
//
#ifndef DAQD_TRUNK_FIXED_SIZE_VECTOR_HH
#define DAQD_TRUNK_FIXED_SIZE_VECTOR_HH
#include <cstddef>
#include <type_traits>
#include <utility>
namespace embedded
{
/*!
* @brief a simple vector with a fixed backing store.
* @details
* @tparam T the type being stored, some constraints are place on T to keep
* the vector noexcept
* @tparam max_size
*/
template < typename T, std::size_t max_size >
class fixed_size_vector
{
static_assert( std::is_nothrow_copy_constructible< T >::value,
"T must be no throw copy constructable" );
static_assert( std::is_nothrow_copy_assignable< T >::value,
"T must be no throw copy assignable" );
static_assert( std::is_nothrow_move_constructible< T >::value,
"T must be no throw move constructable" );
static_assert( std::is_nothrow_move_assignable< T >::value,
"T must be no throw move assignable" );
static_assert( std::is_nothrow_destructible< T >::value,
"T must be no throw destructable" );
using storage_type =
typename std::aligned_storage< sizeof( T ), alignof( T ) >::type;
public:
using value_type = T;
using size_type = std::size_t;
using iterator = typename std::add_pointer< T >::type;
using const_iterator = typename std::add_pointer<
typename std::add_const< T >::type >::type;
~fixed_size_vector( )
{
clear( );
}
size_type
size( ) const noexcept
{
return entries_;
}
bool
empty( ) const noexcept
{
return size( ) == 0;
}
constexpr size_type
capacity( ) const noexcept
{
return max_size;
}
iterator
begin( ) noexcept
{
return reinterpret_cast< T* >( &storage_[ 0 ] );
}
iterator
end( ) noexcept
{
return begin( ) + entries_;
}
const_iterator
begin( ) const noexcept
{
return reinterpret_cast< const_iterator >( &storage_[ 0 ] );
}
const_iterator
end( ) const noexcept
{
return begin( ) + entries_;
}
const_iterator
cbegin( ) const noexcept
{
return begin( );
}
const_iterator
cend( ) const noexcept
{
return end( );
}
/*!
* @brief push an entry onto the back of the vector if space is
* available.
* @param val
* @return true on success, else false (no change made to the vector)
*/
bool
push_back( const T& val ) noexcept
{
if ( size( ) < capacity( ) )
{
T* dest = reinterpret_cast< T* >( &storage_[ entries_ ] );
new ( dest ) T( std::move( val ) );
++entries_;
return true;
}
return false;
}
/*!
* @brief push an entry onto the back of the vector if space is
* available.
* @param val
* @return true on success, else false (no change made to the vector)
*/
bool
push_back( T&& val ) noexcept
{
if ( size( ) < capacity( ) )
{
T* dest = reinterpret_cast< T* >( &storage_[ entries_ ] );
new ( dest ) T( std::move( val ) );
++entries_;
return true;
}
return false;
}
/*!
* @brief emplace an entry onto the back of the vector if space is
* available.
* @tparam Args The argument types to pass to T's constructor
* @param args The argument to pass to T's constructor
* @return true on success, else false (no change made to the vector)
*/
template < typename... Args >
bool
emplace_back( Args... args ) noexcept
{
if ( size( ) < capacity( ) )
{
T* dest = reinterpret_cast< T* >( &storage_[ entries_ ] );
new ( dest ) T( std::forward< Args >( args )... );
++entries_;
return true;
}
return false;
}
T& operator[]( size_type index ) noexcept
{
return *reinterpret_cast< T* >( &storage_[ index ] );
}
const T& operator[]( size_type index ) const noexcept
{
return *reinterpret_cast< T* >( &storage_[ index ] );
}
void
pop_back( ) noexcept
{
if ( !empty( ) )
{
size_type index = entries_ - 1;
T* cur = reinterpret_cast< T* >( &storage_[ index ] );
cur->~T( );
entries_--;
}
}
template < typename T_ = T,
typename std::enable_if<
std::is_trivially_destructible< T_ >::value,
int >::type = 0 >
void
clear( )
{
entries_ = 0;
}
template < typename T_ = T,
typename std::enable_if<
!std::is_trivially_destructible< T_ >::value,
int >::type = 0 >
void
clear( )
{
while ( !empty( ) )
{
pop_back( );
}
}
private:
storage_type storage_[ max_size ]{};
size_type entries_{ 0 };
};
} // namespace embedded
#endif // DAQD_TRUNK_FIXED_SIZE_VECTOR_HH
......@@ -6,8 +6,26 @@
#include "fixed_size_string.hh"
#include <cstring>
#include <type_traits>
#include <vector>
static_assert(
std::is_trivially_copyable< embedded::fixed_string< 64 > >::value,
"Must be trivial" );
static_assert( std::is_standard_layout< embedded::fixed_string< 64 > >::value,
"Must be standard layout" );
static_assert(
std::is_nothrow_constructible< embedded::fixed_string< 64 > >::value,
"Must be nothrow constructable" );
static_assert(
std::is_nothrow_copy_constructible< embedded::fixed_string< 64 > >::value,
"Must be nothrow copy constructable" );
static_assert(
std::is_nothrow_copy_assignable< embedded::fixed_string< 64 > >::value,
"Must be nothrow copy assignable" );
static_assert( std::is_nothrow_swappable< embedded::fixed_string< 64 > >::value,
"Must be nothrow swappable" );
TEST_CASE( "You can create a fixed_size_string" )
{
......
//
// Created by jonathan.hanks on 11/13/20.
//
#include "catch.hpp"
#include "fixed_size_vector.hh"
#include <algorithm>
TEST_CASE( "You can create a fixed size vector" )
{
embedded::fixed_size_vector< int, 20 > v0;
REQUIRE( v0.size( ) == 0 );
REQUIRE( v0.empty( ) );
REQUIRE( v0.capacity( ) == 20 );
REQUIRE( v0.begin( ) == v0.end( ) );
const embedded::fixed_size_vector< int, 20 > v1;
REQUIRE( v1.begin( ) == v1.end( ) );
REQUIRE( v1.cbegin( ) == v1.cend( ) );
REQUIRE( v1.begin( ) == v1.cbegin( ) );
REQUIRE( v1.end( ) == v1.cend( ) );
static_assert(
std::is_same<
int*,
typename embedded::fixed_size_vector< int, 20 >::iterator >::value,
"The iterator is a pointer" );
static_assert(
std::is_same< const int*,
typename embedded::fixed_size_vector< int, 20 >::
const_iterator >::value,
"The iterator is pointer to const" );
}
TEST_CASE( "You can add elements to the back of a fixed size vector" )
{
embedded::fixed_size_vector< int, 20 > v0;
REQUIRE( v0.push_back( 5 ) );
REQUIRE( v0.size( ) == 1 );
REQUIRE( v0.capacity( ) == 20 );
REQUIRE( v0.push_back( 6 ) );
REQUIRE( v0.size( ) == 2 );
REQUIRE( v0.capacity( ) == 20 );
REQUIRE( v0.push_back( 7 ) );
REQUIRE( v0.size( ) == 3 );
REQUIRE( v0.capacity( ) == 20 );
REQUIRE( v0.end( ) == v0.begin( ) + v0.size( ) );
REQUIRE( *v0.begin( ) == 5 );
REQUIRE( *( v0.begin( ) + 1 ) == 6 );
REQUIRE( *( v0.begin( ) + 2 ) == 7 );
}
TEST_CASE( "You can add elements to the back of a fixed size vector with "
"emplace_back" )
{
embedded::fixed_size_vector< int, 20 > v0;
REQUIRE( v0.emplace_back( 5 ) );
REQUIRE( v0.size( ) == 1 );
REQUIRE( v0.capacity( ) == 20 );
REQUIRE( v0.emplace_back( 6 ) );
REQUIRE( v0.size( ) == 2 );
REQUIRE( v0.capacity( ) == 20 );
REQUIRE( v0.emplace_back( 7 ) );
REQUIRE( v0.size( ) == 3 );
REQUIRE( v0.capacity( ) == 20 );
REQUIRE( v0.end( ) == v0.begin( ) + v0.size( ) );
REQUIRE( *v0.begin( ) == 5 );
REQUIRE( *( v0.begin( ) + 1 ) == 6 );
REQUIRE( *( v0.begin( ) + 2 ) == 7 );
}
TEST_CASE( "Attempting to add elements when full returns a false value" )
{
embedded::fixed_size_vector< int, 20 > v0;
for ( auto i = 0; i < v0.capacity( ); ++i )
{
REQUIRE( v0.push_back( i ) );
}
REQUIRE( v0.size( ) == v0.capacity( ) );
REQUIRE( v0.push_back( 42 ) == false );
REQUIRE( v0.emplace_back( 43 ) == false );
REQUIRE( v0.size( ) == v0.capacity( ) );
}
TEST_CASE( "You can access elements via []" )
{
embedded::fixed_size_vector< int, 20 > v0;
for ( auto i = 0; i < v0.capacity( ); ++i )
{
REQUIRE( v0.push_back( i ) );
}
for ( auto i = 0; i < v0.capacity( ); ++i )
{
REQUIRE( v0[ i ] == i );
}
}
TEST_CASE( "You can pop an entry off of the end of a vector" )
{
embedded::fixed_size_vector< int, 20 > v0;
for ( auto i = 0; i < v0.capacity( ); ++i )
{
REQUIRE( v0.push_back( i ) );
}
REQUIRE( v0.end( ) == v0.begin( ) + v0.capacity( ) );
REQUIRE( v0.size( ) == v0.capacity( ) );
v0.pop_back( );
REQUIRE( v0.end( ) == v0.begin( ) + v0.capacity( ) - 1 );
REQUIRE( v0.size( ) == v0.capacity( ) - 1 );
}
TEST_CASE( "You can clear a fixed sized vector to empty it" )
{
embedded::fixed_size_vector< int, 20 > v0;
for ( auto i = 0; i < v0.capacity( ); ++i )
{
REQUIRE( v0.push_back( i ) );
}
REQUIRE( v0.end( ) == v0.begin( ) + v0.capacity( ) );
REQUIRE( v0.size( ) == v0.capacity( ) );
v0.clear( );
REQUIRE( v0.size( ) == 0 );
REQUIRE( v0.begin( ) == v0.end( ) );
REQUIRE( v0.capacity( ) == 20 );
}
class DestructionNotifier
{
public:
explicit DestructionNotifier( int* counter_dest ) : counter_{ counter_dest }
{
}
DestructionNotifier( const DestructionNotifier& ) noexcept = default;
DestructionNotifier&
operator=( const DestructionNotifier& ) noexcept = default;
~DestructionNotifier( )
{
( *counter_ )++;
}
private:
int* counter_;
};
TEST_CASE( "Destructors are called on items in the fixed size vector when it "
"is destroyed or elements are popped off" )
{
int counter = 0;
{
embedded::fixed_size_vector< DestructionNotifier, 10 > v0;
v0.emplace_back( &counter );
REQUIRE( !v0.empty( ) );
REQUIRE( counter == 0 );
v0.pop_back( );
REQUIRE( v0.empty( ) );
REQUIRE( counter == 1 );
counter = 0;
while ( v0.emplace_back( &counter ) )
{
}
REQUIRE( v0.size( ) == v0.capacity( ) );
}
REQUIRE( counter == 10 );
}
TEST_CASE( "We have defined enough to make the this usable by std::algorithms" )
{
embedded::fixed_size_vector< int, 20 > v0;
v0.emplace_back( 5 );
v0.emplace_back( 4 );
v0.emplace_back( 3 );
v0.emplace_back( 2 );
v0.emplace_back( 1 );
std::sort( v0.begin( ), v0.end( ) );
REQUIRE( v0[ 0 ] == 1 );
REQUIRE( v0[ 1 ] == 2 );
REQUIRE( v0[ 2 ] == 3 );
REQUIRE( v0[ 3 ] == 4 );
REQUIRE( v0[ 4 ] == 5 );
}
// static_assert(std::is_trivially_destructible<embedded::fixed_size_vector<int,
// 20> >::value, "For trivial destructable types the fixed sized vector should
// be trivially destructable");
static_assert(
!std::is_trivially_destructible<
embedded::fixed_size_vector< DestructionNotifier, 10 > >::value,
"for non-trivially destructable types the fixed size vector is not "
"trivially destructable" );
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment