...
 
Commits (395)
# common configuration parts
.common: &common
before_script:
- rm -rf .git # ups
tags:
- shared-fi
.debian: &debian
image: cxx:latest
script:
- apt-get update >& apt.log
- make prerequisites >& prerequisites.log
- make toolchain >& toolchain.log
- make CMAKE_EXTRA="-DOPT_Z3=OFF" >& build.log
- make unit >& unit.log
- make functional >& functional.log
artifacts:
paths:
- "*.log"
expire_in: 1 month
when: always
# actual builds
build_clang:
<<: *common
<<: *debian
variables:
CC: "clang"
CXX: "clang++"
build_gcc:
<<: *common
<<: *debian
variables:
CC: "gcc"
CXX: "g++"
......@@ -2,6 +2,7 @@ GENERATOR != if ninja --version > /dev/null 2>&1 || \
ninja-build --version > /dev/null 2>&1; then echo Ninja; else echo Unix Makefiles; fi
# fallback
GENERATOR ?= Unix Makefiles
.CURDIR ?= $(CURDIR)
-include /etc/divine.make # for system-wide config
-include local.make
......@@ -14,7 +15,7 @@ PREFIX ?= /opt/divine
RELEASE_BUILD_TYPE ?= RelWithDebInfo
MAKEFLAGS ?= --no-print-directory
OBJ ?= $(PWD)/_build.
OBJ ?= $(.CURDIR)/_build.
BENCH_NAME ?= $(LOGNAME)
EXTRA != if test "$(GENERATOR)" = Ninja && test -n "$(VERBOSE)"; then echo -v -d explain; fi; \
if test -n "$(JOBS)"; then echo -j $(JOBS); fi
......@@ -22,7 +23,7 @@ EXTRA != if test "$(GENERATOR)" = Ninja && test -n "$(VERBOSE)"; then echo -v -d
TOOLDIR = $(OBJ)toolchain
CLANG = $(TOOLDIR)/clang/
RTBIN = $(TOOLDIR)/dios
RTSRC = $(PWD)/dios
RTSRC = $(.CURDIR)/dios
LIBUNWIND_LDIR = $(RTBIN)/libunwind/src
CXX_LDIR = $(TOOLDIR)/lib
......@@ -40,7 +41,7 @@ CXXFLAGS_ = -isystem $(RTSRC)/libcxxabi/include -isystem $(RTSRC)/libcxx/include
TOOLCHAIN_ = CMAKE_C_COMPILER=$(CLANG)/bin/clang;CMAKE_CXX_COMPILER=$(CLANG)/bin/clang++;\
CMAKE_CXX_FLAGS=$(CXXFLAGS_)
TOOLCHAIN ?= $(TOOLCHAIN_);CMAKE_EXE_LINKER_FLAGS=$(LDFLAGS_);CMAKE_SHARED_LINKER_FLAGS=$(LDFLAGS_)
TOOLSTAMP ?= $(TOOLDIR)/stamp-v7
TOOLSTAMP ?= $(TOOLDIR)/stamp-v8
CONFIG += CMAKE_INSTALL_PREFIX=${PREFIX}
static_FLAGS += CMAKE_BUILD_TYPE=Release;$(TOOLCHAIN);$(CONFIG);BUILD_SHARED_LIBS=OFF;STATIC_BUILD=ON
......@@ -67,6 +68,7 @@ NORMAL = divine unit functional website check llvm-utils clang \
test-divine test-lart test-bricks shoop
TARGETS = $(NORMAL) $(SPECIAL)
functional: divcheck
${NORMAL}:
$(MAKE) $(DEFAULT_FLAVOUR)-$@
......@@ -83,7 +85,7 @@ divbench-install:
sh releng/install-divbench.sh $(OBJ)bench/tools/divbench $(BENCH_DIR) $(BENCH_NAME) \
$(SKIP_SCHEDULE)
SETENV = env BUILD_RPATH=$(BUILD_RPATH) TESTHOOK="$(TESTHOOK)" JOBS="$(JOBS)" TEST_ALLOW_DOWNLOADS="$(TEST_ALLOW_DOWNLOADS)"
SETENV = env BUILD_RPATH=$(BUILD_RPATH) TESTHOOK="$(TESTHOOK)" JOBS="$(JOBS)" TEST_ALLOW_DOWNLOADS="$(TEST_ALLOW_DOWNLOADS)" OBJ="$(OBJ)"
OF = $(OBJ)$(FLAVOUR)/
DIRENV_PATH = $(OF)/tools:$(OF)/llvm/bin:$(OBJ)bench/tools:$(OF)/divine:$(OF)/lart:$(OF)
......@@ -94,7 +96,7 @@ config:
$(CMAKE) -E copy_if_different $(OBJ)$(FLAVOUR)/config.tmp $(OBJ)$(FLAVOUR)/config.vars
if ! test -e $(OBJ)$(FLAVOUR)/config.done || test -n "$(FORCE_CMAKE)"; then \
chmod +x test/divine; \
cd $(OBJ)$(FLAVOUR) && $(CMAKE) $(PWD) $(CMAKE_EXTRA) -G "$(GENERATOR)" && \
cd $(OBJ)$(FLAVOUR) && $(CMAKE) $(.CURDIR) $(CMAKE_EXTRA) -G "$(GENERATOR)" && \
touch $(OBJ)$(FLAVOUR)/config.done; fi
build: config
......
......@@ -19,9 +19,12 @@
#pragma once
#include "brick-trace"
#include <exception>
#include <unistd.h>
#ifdef __divine__
#include <sys/fault.h>
#endif
/* This file provides an assortment of assertion macros which provide
* additional information about the circumstances of assertion failures, for
* instance ASSERT_EQ will print both the source code statement and the actual
......@@ -47,35 +50,39 @@
#ifdef NDEBUG
#define ASSERT(...) static_cast< decltype(__VA_ARGS__, void(0)) >(0)
#define ASSERT_PRED(p, x) static_cast< decltype(p, x, void(0)) >(0)
#define ASSERT_EQ(x, y) static_cast< decltype(x, y, void(0)) >(0)
#define ASSERT_LEQ(x, y) static_cast< decltype(x, y, void(0)) >(0)
#define ASSERT_LT(x, y) static_cast< decltype(x, y, void(0)) >(0)
#define ASSERT_NEQ(x, y) static_cast< decltype(x, y, void(0)) >(0)
#define ASSERT(...) static_cast< decltype(__VA_ARGS__, void(0)) >(0)
#define ASSERT_PRED(...) static_cast< decltype(__VA_ARGS__, void(0)) >(0)
#define ASSERT_EQ(...) static_cast< decltype(__VA_ARGS__, void(0)) >(0)
#define ASSERT_LEQ(...) static_cast< decltype(__VA_ARGS__, void(0)) >(0)
#define ASSERT_LT(...) static_cast< decltype(__VA_ARGS__, void(0)) >(0)
#define ASSERT_NEQ(...) static_cast< decltype(__VA_ARGS__, void(0)) >(0)
#else
#define ASSERT(...) BRICK_ASSERT( bool, BRICK_SHARP_FIRST( __VA_ARGS__, ignored ), __VA_ARGS__ )
#define ASSERT_P(p, x) BRICK_ASSERT( pred, #p "( " #x " )", x, p( x ) )
#define ASSERT_EQ(x, y) BRICK_ASSERT( eq, #x " == " #y, x, y )
#define ASSERT_LT(x, y) BRICK_ASSERT( lt, #x " < " #y, x, y )
#define ASSERT_LEQ(x, y) BRICK_ASSERT( leq, #x " <= " #y, x, y )
#define ASSERT_NEQ(x, y) BRICK_ASSERT( neq, #x " != " #y, x, y )
#define BRICK_ASSERT_BIN(op, inv, x, ...) \
BRICK_ASSERT( bin, \
#x " " #op " " BRICK_SHARP_FIRST( __VA_ARGS__, ignored ), \
[]( const auto &a, const auto &b ) { return a op b; }, \
#inv, x, __VA_ARGS__ )
#define ASSERT(...) BRICK_ASSERT( bool, BRICK_SHARP_FIRST( __VA_ARGS__, ignored ), __VA_ARGS__ )
#define ASSERT_P(p, x) BRICK_ASSERT( pred, #p "( " #x " )", x, p( x ) )
#define ASSERT_EQ(...) BRICK_ASSERT_BIN( ==, !=, __VA_ARGS__ )
#define ASSERT_LT(...) BRICK_ASSERT_BIN( <, >=, __VA_ARGS__ )
#define ASSERT_LEQ(...) BRICK_ASSERT_BIN( <=, >, __VA_ARGS__ )
#define ASSERT_NEQ(...) BRICK_ASSERT_BIN( !=, ==, __VA_ARGS__ )
#endif
/* you must #include <brick-string> to use UNREACHABLE_F */
#define UNREACHABLE_F(...) BRICK_ASSERT( die, "", ::brick::string::fmtf(__VA_ARGS__) )
#define UNREACHABLE(...) BRICK_ASSERT( die, "", __VA_ARGS__ )
#define UNREACHABLE_() BRICK_ASSERT( die, "", "" )
#define NOT_IMPLEMENTED() BRICK_ASSERT( die, "", "missing implementation" )
#define BRICK_LOCATION(stmt) ::brq::trace_location{ __LINE__, __FILE__, stmt }
namespace brq
{
struct assert_failed : std::exception
struct assert_failed
{
string_builder str;
......@@ -125,6 +132,18 @@ namespace brq
const char *what() const noexcept { return str.buffer(); }
};
[[noreturn]] static inline void assert_signal( assert_failed &err )
{
#if defined(__divine__)
if ( err.str.truncated() )
__vm_cancel();
__dios_fault( _VM_F_Assert, err.what() );
__builtin_trap();
#else
throw std::move( err );
#endif
}
template< typename X, typename... Y >
void assert_bool_fn( const trace_location &l, const X &x, const Y & ... y )
{
......@@ -132,7 +151,7 @@ namespace brq
return;
assert_failed f( l );
format_args( f, y... );
throw f;
assert_signal( f );
}
template< typename... T >
......@@ -141,43 +160,29 @@ namespace brq
{
assert_failed f( l, "unreachable executed:" );
format_args( f, args... );
throw f;
assert_signal( f );
}
#define ASSERT_FN(name, op, inv) \
__boring static inline \
void assert_ ## name ## _fn( const trace_location &l, int64_t x, int64_t y ) \
{ \
if ( !( x op y ) ) \
{ \
assert_failed f( l ); \
f << "\n but got " \
<< x << " " #inv " " << y << "\n"; \
throw f; \
} \
/* trace_fn( l, "assert passed:", l._statement, "[", x, #op, y, "]" ); */ \
} \
template< typename X, typename Y > \
__boring auto assert_ ## name ## _fn( const trace_location &l, const X &x, const Y &y ) \
-> typename std::enable_if< \
!std::is_integral< X >::value || \
!std::is_integral< Y >::value >::type \
{ \
if ( !( x op y ) ) \
{ \
assert_failed f( l ); \
f << "\n but got " \
<< x << " " #inv " " << y << "\n"; \
throw f; \
} \
/* trace_fn( l, "assert passed:", l._statement, "[", x, #op, y, "]" ); */ \
template< typename Op, typename A, typename B, typename... T >
void assert_bin_fn( const trace_location &l, Op op, std::string_view inv,
const A &a, const B &b, const T & ... args )
{
bool pass;
if constexpr ( std::is_integral_v< A > && std::is_integral_v< B > )
pass = op( static_cast< std::common_type_t< A, B > >( a ),
static_cast< std::common_type_t< A, B > >( b ) );
else
pass = !!op( a, b );
if ( !pass )
{
assert_failed f( l );
f << "\n but got " << a << " " << inv << " " << b << "\n";
format_args( f, args... );
assert_signal( f );
}
/* trace_fn( l, "assert passed:", l._statement, "[", x, op_str, y, "]" ); */
}
ASSERT_FN(eq, ==, !=);
ASSERT_FN(leq, <=, >);
ASSERT_FN(lt, <, >=);
ASSERT_FN(neq, !=, ==);
template< typename X >
__boring void assert_pred_fn( const trace_location &l, X x, bool p )
{
......@@ -185,7 +190,7 @@ namespace brq
{
assert_failed f( l );
f << "\n but got x = " << x << "\n";
throw f;
assert_signal( f );
}
}
}
......
......@@ -188,7 +188,7 @@ struct BenchmarkBase : unittest::TestBase
virtual std::string group() { return ""; }
int64_t p, q;
BenchmarkBase( std::string n ) : TestBase( n ) {}
BenchmarkBase( std::string n ) : TestBase( n, "" ) {}
};
struct Group
......
......@@ -251,7 +251,7 @@ namespace brick::bitlevel
template< typename number >
static inline number fill( number x )
{
static const unsigned m = sizeof( number ) * 8;
const unsigned m = sizeof( number ) * 8;
unsigned r = 1;
if ( !x )
return 0;
......@@ -276,20 +276,23 @@ namespace brick::bitlevel
}
template<>
inline unsigned MSB< unsigned int >( unsigned int x ) {
static const unsigned long bits = sizeof( unsigned int ) * 8 - 1;
inline unsigned MSB< unsigned int >( unsigned int x )
{
const unsigned long bits = sizeof( unsigned int ) * 8 - 1;
return bits - __builtin_clz( x );
}
template<>
inline unsigned MSB< unsigned long >( unsigned long x ) {
static const unsigned bits = sizeof( unsigned long ) * 8 - 1;
inline unsigned MSB< unsigned long >( unsigned long x )
{
const unsigned bits = sizeof( unsigned long ) * 8 - 1;
return bits - __builtin_clzl( x );
}
template<>
inline unsigned MSB< unsigned long long >( unsigned long long x ) {
static const unsigned bits = sizeof( unsigned long long ) * 8 - 1;
inline unsigned MSB< unsigned long long >( unsigned long long x )
{
const unsigned bits = sizeof( unsigned long long ) * 8 - 1;
return bits - __builtin_clzll( x );
}
......@@ -420,7 +423,7 @@ namespace brick::bitlevel
template< typename T, int width = sizeof( T ) * 8 >
struct BitField
{
static const int bitwidth = width;
static constexpr int bitwidth = width;
struct Virtual : BitPointer
{
T set( T t ) { bitcopy( BitPointer( &t ), *this, bitwidth ); return t; }
......@@ -501,7 +504,7 @@ namespace brick::bitlevel
struct BitLock
{
static const int bitwidth = 1;
static constexpr int bitwidth = 1;
struct Virtual : BitPointer {
using Atomic = std::atomic< uint32_t >;
Atomic &atomic() { return *reinterpret_cast< Atomic * >( &word() ); }
......@@ -526,8 +529,8 @@ namespace brick::bitlevel
template< int O, typename T, typename... Args >
struct BitAccess< O, T, Args... >
{
static const int offset = O;
static const int width = T::bitwidth;
static constexpr int offset = O;
static constexpr int width = T::bitwidth;
typedef typename T::Virtual Head;
typedef BitAccess< offset + T::bitwidth, Args... > Tail;
static const int total = width + Tail::total;
......@@ -543,7 +546,7 @@ namespace brick::bitlevel
struct _BitTuple
{
using Access = BitAccess< 0, Args... >;
static const int bitwidth = Access::total;
static constexpr int bitwidth = Access::total;
template< int I > using AccessAt = _AccessAt< Access, I >;
template< int I > static int offset() { return AccessAt< I >::T::offset; }
};
......
......@@ -111,7 +111,7 @@ struct Validator< Parse, Ps... >: ValidatorInterface
{
if ( _name != t )
return _next->parse( edit, t, v );
auto fail = []( auto, std::string s ) { throw except::Error( "parsing command line: " + s ); };
auto fail = []( auto, std::string s ) { brq::raise() << "parsing command line: " << s; };
return _parse( v, Push< T >( edit ), fail );
}
......@@ -344,7 +344,7 @@ struct Pattern : brq::refcount_base<>
for ( int i = 0; i < int( res.size() ); ++i )
if ( res[i] == next )
alts += "\n matched " + _subs[i]->fmt();
throw except::Error( "Ambiguous parse:" + line + alts );
brq::raise() << "ambiguous parse:" << line << alts;
}
}
......@@ -427,14 +427,14 @@ struct Pattern : brq::refcount_base<>
if ( !rest.empty() )
{
auto n = new Pattern( _validator );
auto n = brq::make_refcount< Pattern >( _validator );
if ( n->create( rest, alt ? this : prev_alt ) )
_next.reset( n );
_next = n;
if ( prev_alt == this && _alt )
{
ASSERT( _type != Empty );
Ptr a( new Pattern( *this ) );
auto a = brq::make_refcount< Pattern >( *this );
*this = Pattern( _validator );
_type = Group;
while ( a )
......@@ -664,7 +664,7 @@ struct OptionSet
return;
if ( parsed.count( &o ) && ( o.flags() & OptionFlag::Unique ) )
throw except::Error( "Option " + o.fmt() + " given more than once\n" );
brq::raise() << "option" << o.fmt() << "given more than once";
parsed.insert( &o );
b = o.parse( validator, t, b, e );
......@@ -679,7 +679,7 @@ struct OptionSet
{
alternatives << "Ambiguous option " << *b << ":";
each_opt( format );
throw except::Error( alternatives.buffer() );
brq::raise() << alternatives.buffer();
}
if ( selected.empty() )
......@@ -691,8 +691,7 @@ struct OptionSet
auto check_required = [&]( auto &o )
{
if ( ( o.flags() & OptionFlag::Required ) && !parsed.count( &o ) )
throw except::Error( "Missing option: " + o.fmt() + ", expected:\n"
+ this->describe() );
brq::raise() << "missing option: " << o.fmt() << ", expected:\n" << this->describe();
};
each_opt( check_required );
......@@ -839,7 +838,7 @@ struct Parser : ParserBase
{
brq::refcount_ptr< Validator > _validator;
Cmds _cmds;
using Result = typename Cmds::template map< GetT >::co;
using Result = typename Cmds::template map_t< GetT >::co;
std::pair< Result, Tokens::iterator > do_parse( Tokens::iterator b, Tokens::iterator e )
{
......@@ -881,7 +880,7 @@ struct Parser : ParserBase
}
else
_cmds.each( [&]( auto &cmd ) { if ( cmd.name() == name ) s << cmd.describe(); } );
return s.buffer();
return s.buffer() ? s.buffer() : "";
}
auto parse( Tokens::iterator b, Tokens::iterator e, std::vector<std::string> &unexpected )
......@@ -919,7 +918,7 @@ struct Parser : ParserBase
for ( std::string str : unexpected )
err << "'" << str << "' ";
}
throw except::Error( err.buffer() );
brq::raise() << err.buffer();
}
return _result;
}
......@@ -960,9 +959,30 @@ auto make_parser( brq::refcount_ptr< V > v )
return Parser< V, brq::nil >( v, brq::nil() );
}
struct Help
{
std::string _cmd;
template< typename P >
void run( std::string_view argv_0, P cmds )
{
std::string description = cmds.describe( _cmd );
if ( description.empty() && !_cmd.empty() )
brq::raise() << "Unknown command '" << _cmd << "'. Available commands are:\n"
<< cmds.describe();
if ( _cmd.empty() )
{
std::cerr << "To print details about a specific command, run '"
<< argv_0 << " help {command}'.\n\n";
std::cerr << cmds.describe() << std::endl;
}
else std::cerr << description << std::endl;
}
};
namespace {
std::vector< std::string > from_argv( int argc, const char **argv )
std::vector< std::string > from_argv( int argc, const char * const *argv )
{
std::vector< std::string > args;
std::copy( argv + 1, argv + argc, std::back_inserter( args ) );
......
......@@ -28,17 +28,30 @@ namespace brq
struct nil
{
using co = nil;
template< template< typename > class f > using map = nil;
template< template< typename > class f > using map_t = nil;
template< typename F >
void each( const F & ) {}
void each( const F & ) const {}
template< typename... Fs >
auto match( Fs... ) { return std::nullopt; }
template< template< typename > class P >
constexpr nil filter() const { return {}; }
template< typename F >
nil map( F ) const { return {}; }
template< typename L >
auto cat( const L &l ) const { return l; }
};
template< typename, typename > struct cons;
template< typename, typename > struct ns;
template< typename A, typename B >
auto make_cons( const A &a, const B &b ) { return cons< A, B >( a, b ); }
template< typename A, typename B >
struct cons
{
......@@ -46,7 +59,7 @@ namespace brq
using cdr_t = B;
using co = ns< car_t, typename cdr_t::co >;
template< template< typename > class f >
using map = cons< f< car_t >, typename cdr_t::template map< f > >;
using map_t = cons< f< car_t >, typename cdr_t::template map_t< f > >;
car_t car;
cdr_t cdr;
......@@ -58,13 +71,64 @@ namespace brq
cdr.each( f );
}
template< typename F >
void each( const F &f ) const
{
f( car );
cdr.each( f );
}
cons( const car_t &car, const cdr_t &cdr )
: car( car ), cdr( cdr )
{}
constexpr auto reverse() const
{
nil rev;
return cons_reverse( *this, rev );
}
template< template< typename > class P >
constexpr auto filter() const
{
if constexpr ( P< car_t >::value )
return make_cons( car, cdr.template filter< P >() );
else
return cdr.template filter< P >();
}
template< typename F >
auto map( F f )
{
auto car_ = f( car );
auto cdr_ = cdr.map( f );
return make_cons( car_, cdr_ );
}
template< typename L >
auto cat( const L &l )
{
return make_cons( car, cdr.cat( l ) );
}
template< typename arg_t, typename... args_t >
cons( const arg_t &arg, const args_t&... args ) : car( arg ), cdr( args... ) {}
cons() = default;
};
template< typename D >
constexpr auto cons_reverse( nil, const D &rev )
{
return rev;
}
template< typename C, typename D >
constexpr auto cons_reverse( const C &cell, const D &rev )
{
return cons_reverse( cell.cdr, cons( cell.car, rev ) );
}
static auto cons_list() { return nil(); }
template< typename T, typename... Ts >
......@@ -74,6 +138,15 @@ namespace brq
return cons< T, decltype( tail ) >( t, tail );
}
template< typename... Ts >
using cons_list_t_ = decltype( cons_list( std::declval< Ts >()... ) );
template< typename... Ts >
struct cons_list_t: cons_list_t_< Ts... >
{
using cons_list_t_< Ts... >::cons_list_t_;
};
template< typename car_t_, typename cdr_t_ >
struct ns
{
......
......@@ -785,128 +785,8 @@ struct InsertSort
}
};
template < typename Key, typename Val,
template< typename T > class Sort_ = StdSort,
typename Container = std::vector< std::pair< Key, Val > > >
class ArrayMap : brick::types::Ord
{
public:
using key_type = Key;
using mapped_type = Val;
using value_type = std::pair< Key, Val >;
using Sort = Sort_< value_type >;
using size_type = typename Container::size_type;
using iterator = typename Container::iterator;
using const_iterator = typename Container::const_iterator;
using reference = value_type&;
using const_reference = const value_type&;
auto begin() { return _container.begin(); }
auto begin() const { return _container.cbegin(); }
auto cbegin() const { return _container.cbegin(); }
auto end() { return _container.end(); }
auto end() const { return _container.cend(); }
auto cend() const { return _container.cend(); }
auto empty() const { return _container.empty(); }
auto size() const { return _container.size(); }
void clear() noexcept { _container.clear(); }
std::pair< iterator, bool > insert( const_reference value ) {
auto it = find( value.first );
if ( it != cend() )
return { it, false };
_container.push_back( value );
Sort::sort( begin(), end() );
return { find( value.first ), true };
}
template< class... Args >
std::pair< iterator, bool > emplace( Args&&... args ) {
_container.emplace_back( std::forward< Args >( args )... );
auto it = Sort::lower_bound( begin(), end() - 1, _container.back() );
if ( it == end() - 1 || it->first != _container.back().first ) {
auto key = _container.back().first;
Sort::sort( begin(), end() );
return { find( key ), true };
}
auto key = it->first;
_container.pop_back();
return { find( key ), false };
}
void erase( iterator pos ) {
using std::swap;
swap( *pos, _container.back() );
_container.pop_back();
Sort::sort( begin(), end() );
}
size_type erase( const key_type& key ) {
auto it = find( key );
if ( it == end() )
return 0;
erase( it );
return 1;
}
void swap( ArrayMap& other ) {
using std::swap;
swap( _container, other._container );
}
size_type count( const Key& key ) const {
return find( key ) == cend() ? 0 : 1;
}
iterator find( const Key& key ) {
auto elem = Sort::lower_bound( begin(), end(), std::make_pair( key, Val() ) );
return elem != end() && elem->first == key ? elem : end();
}
const_iterator find( const Key& key ) const {
auto elem = Sort::lower_bound( cbegin(), cend(), std::make_pair( key, Val() ) );
return elem != cend() && elem->first == key ? elem : cend();
}
Val& operator[]( const Key& key ) {
auto it = find( key );
if ( it != end() )
return it->second;
return emplace( key, Val{} ).first->second;
}
Val& at( const Key& key ) {
auto it = find( key );
if ( it == end() )
throw std::out_of_range( "ArrayMap::at" );
return it->second;
}
const Val& at( const Key& key ) const {
auto it = find( key );
if ( it == cend() )
throw std::out_of_range( "ArrayMap::at" );
return it->second;
}
bool operator<( const ArrayMap &o ) const {
return _container < o._container;
}
bool operator==( const ArrayMap &o ) const {
return _container == o._container;
}
private:
Container _container;
};
}
#ifdef BRICK_UNITTEST_REG
namespace t_data {
using namespace brick::data;
......@@ -1920,79 +1800,8 @@ struct TestSmallVector {
}
};
template< template< typename T > class Sort >
struct TestArrayMap {
TEST( simple ) {
ArrayMap< std::string, std::string, Sort > x;
ASSERT( x.insert( std::make_pair( "bKey", "bVal" ) ).second );
ASSERT( !x.insert( std::make_pair( "bKey", "bVal" ) ).second );
ASSERT_EQ( x.size(), 1 );
ASSERT_EQ( x.find( "bKey" )->second, "bVal" );
ASSERT( x.emplace( "aKey", "aVal" ).second );
ASSERT( !x.emplace( "aKey", "aVal" ).second );
ASSERT_EQ( x.size(), 2 );
ASSERT_EQ( x.find( "aKey" )->second, "aVal" );
ASSERT_EQ( x.find( "bKey" )->second, "bVal" );
x[ "cKey" ] = "cVal";
ASSERT_EQ( x.size(), 3 );
ASSERT_EQ( x.find( "aKey" )->second, "aVal" );
ASSERT_EQ( x.find( "bKey" )->second, "bVal" );
ASSERT_EQ( x.find( "cKey" )->second, "cVal" );
x.erase( "bKey" );
ASSERT_EQ( x.find( "aKey" )->second, "aVal" );
ASSERT_EQ( x.find( "cKey" )->second, "cVal" );
ASSERT_EQ( x.at( "aKey" ), "aVal" );
ASSERT_EQ( x.at( "cKey" ), "cVal" );
}
TEST(outOfRange) {
ArrayMap< int, std::string, Sort > am;
am[ 1 ] = "aKey";
try {
am.at( 2 );
ASSERT( false );
} catch ( std::out_of_range & ) { }
}
TEST(comparison) {
ArrayMap< int, int, Sort > m;
m.emplace( 1, 1 );
m.emplace( 2, 1 );
auto m2 = m;
ASSERT( m == m2 );
ASSERT( !(m != m2) );
ASSERT( m <= m2 );
ASSERT( m2 <= m );
ASSERT( !(m < m2) );
ASSERT( !(m2 < m) );
m2.emplace( 3, 1 );
ASSERT( m != m2 );
ASSERT( m <= m2 );
ASSERT( m < m2 );
m2.erase( m2.find( 3 ) );
m2[ 2 ] = 2;
ASSERT( m != m2 );
ASSERT( m <= m2 );
ASSERT( m < m2 );
}
};
template struct TestArrayMap< StdSort >;
template struct TestArrayMap< InsertSort >;
#undef TC
}
#endif
}
......
......@@ -18,27 +18,61 @@
#pragma once
#include "brick-trace"
#include <stdexcept>
#include <system_error>
#include <string>
#include <tuple>
namespace brick {
namespace except {
/*
* This is a base class for exceptions which arise from improper use at the
* user level (as opposed to programmer level). Use this if you expect an
* exception to be seen by actual users.
*/
struct Error : std::runtime_error
namespace brq
{
int _exit;
Error( std::string err, int exit = 1 )
: std::runtime_error( err ), _exit( exit )
{}
};