cpp_util is a header-only C++11 library. It is a collection of some small tools which were created to simplify application developement. Some of them are rather old and even ancient, created even before C++98 was standartized. Some are relative new and require C++ compilers with good support of C++11 standard.
Just clone Hg repository somewhere and add path to dev
folder to your
INCLUDE path. For example:
cd ~/projects
git clone https://github.com/Stiffstream/cpp-util
export CPLUS_INCLUDE_PATH=$(CPLUS_INCLUDE_PATH):~/projects/cpp_util-3.0/dev
or
cd c:\projects
git clone https://github.com/Stiffstream/cpp-util
set INCLUDE=%INCLUDE%;c:\projects\cpp_util-3.0\dev
Inclusion of cpp_util via MxxRu::externals can be done with recipes like these:
MxxRu::arch_externals :cpp_util_3 do |e|
e.url 'https://github.com/Stiffstream/cpp-util/archive/v3.0.0-rc9.tar.gz'
e.map_dir 'dev/cpp_util_3' => 'dev'
end
Or
MxxRu::hg_externals :cpp_util_3 do |e|
e.url 'https://github.com/Stiffstream/cpp-util'
e.tag 'v.3.0.0-rc9'
e.map_dir 'dev/cpp_util_3' => 'dev'
end
Mxx_ru build tool is necessary for building samples and tests. To install Mxx_ru you need Ruby and RubyGems installed in your system (in most cases RubyGems is installed with Ruby automatically). Then:
gem install Mxx_ru
To build all tests and samples it is necessary to run ruby build.rb
from
dev
folder:
cd ~/projects
hg clone https://github.com/Stiffstream/cpp-util
cd cpp-util/dev
ruby build.rb
To build some specific sample or test it is necessary to run corresponding
project file from dev
folder:
cd ~/projects
hg clone https://github.com/Stiffstream/cpp-util
cd cpp-util/dev
ruby sample/lexcast/util/prj.rb
Note. Mxx_ru tries to detect type of your compiler automatically. In most
cases it just works. But sometimes it is necessary to specify your compiler
manually via MXX_RU_CPP_TOOLSET
environment variable. For example if you have
GCC and clang in your Linux and want to use clang, then MXX_RU_CPP_TOOLSET
must be set as:
export MXX_RU_CPP_TOOLSET=clang_linux
cpp_util is distributed under 3-clauses BSD license.
Helper function at_scope_exit
. Something like scope_exit
from D language
or defer
from Go:
#include <cpp_util_3/at_scope_exit.hpp>
...
void dump_content_to_file( const char * file_name, const content & cnt )
{
std::FILE * file = std::fopen( file_name, "w" );
if( file )
{
auto file_closer = cpp_util_3::at_scope_exit( [file]{ std::fclose( file ); } );
...
}
}
Definition for macros like CPP_UTIL_3_EXPORT
, CPP_UTIL_3_IMPORT
,
CPP_UTIL_3_UNIX
, CPP_UTIL_3_WINDOWS
, CPP_UTIL_3_WIN64
and so on.
Macros CPP_UTIL_3_EXPORT
and CPP_UTIL_3_IMPORT
hide compiler-specific
keywords for exporting/importing functions from dynamic-link libraries:
// Somewhere in DLL-related header file...
#pragma once
...
#include <cpp_util_3/detect_compiler.hpp>
#if defined(MY_DLL)
// Header included during compilation of the DLL.
// Symbols must be exported.
#define MY_DLL_DECLSPEC CPP_UTIL_3_EXPORT
#else
// Header included during compilation of a DLL client.
// Symbols must be imported.
#define MY_DLL_DECLSPEC CPP_UTIL_3_IMPORT
#endif
...
class MY_DLL_DECLSPEC some_exported_class { ... };
MY_DLL_DECLSPEC void some_exported_function();
Helper function ensure
for simplification of checking for some condition
and throwing an exception if that condition is not fulfilled:
#include <cpp_util_3/ensure.hpp>
...
// Do some 3rd-party library call.
auto r = mosquitto_subscribe( m_mosq.get(),
&mid, topic_name.c_str(), qos_to_use );
// Expect only a subset of error codes.
// If another error code is returned then exception must be thrown.
cpp_util_3::ensure< std::runtime_error >(
MOSQ_ERR_SUCCESS == r || MOSQ_ERR_NO_CONN == r || MOSQ_ERR_CONN_LOST == r,
[&]{
return tfm::format( "mosquitto_subscribe(%s, %d) failed, rc=%d",
topic_name, qos_to_use, r );
} );
Helper function terminate_if_throws
is intended to be used in cases where
some block of code must be run without any exceptions. If an exception is thrown
from that block then the whole application must be terminated.
#include <cpp_util_3/terminate_if_throws.hpp>
...
// We want to provide strong exception guarantee for that method.
void some_complex_class::do_some_modification(const params & p) {
// Checks all necessary conditions first.
// Some exceptions can be thrown here.
check_condition_one(p);
check_condition_two(p);
...
// Preallocate some resources.
// Exceptions are expected here. But this is not a problem
// because there is no any actual state changes yet.
auto r1 = preallocate_resource_one(p);
auto r2 = preallocate_resource_two(p);
...
// All preparations are done. We don't expect exceptions
// in the following block of code. But if some exception is thrown
// then we don't know how to repair from it.
cpp_util_3::terminate_if_throws( [&] {
do_state_change_action_one(...);
do_state_change_action_two(...);
...
} );
}
Helper class and related stuff for lifetime indication via type:
class config { ... };
class data_processor {
// Indicator that reference to config must outlive data_processor object.
cpp_util_3::outliving_reference_t<const config> cfg_;
public :
data_processor(
// Indicator that reference to config must outlive data_processor object.
cpp_util_3::outliving_reference_t<const config> cfg)
: cfg_(cfg)
{...}
...
};
void f() {
config cfg = load_config();
data_processor processor( cpp_util_3::outliving_const(cfg) );
...
}
Family of functions lexcast
and slexcast
. They are very old and now it
is better to use more modern and efficient alternatives (Boost.LexicalCast,
cppformat or tinyformat). Functions lexcast
/slexcast
are still in
cpp_util library mostly by reasons of compatibility.
But there are some lexcast helpers which can be useful even now.
Several tools like hex
, hex_0x
and all
. For example, it is possible to
show content of a vector by help from cppformat and cpp_util:
#include <cppformat/format.h>
#include <cpp_util_3/lexcasts/util.hpp>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
using namespace cpp_util_3;
int main()
{
vector< int > v{ 1, 2, 3, 4, 5, 6 };
fmt::print( "vector is: {}\n", lexcasts::all( v, ", " ) );
fmt::print( "vector is: {}\n", lexcasts::all( v, ", ", lexcasts::hex_0x() ) );
fmt::print( "items greater than 3: {}\n",
lexcasts::all(
upper_bound( begin(v), end(v), 3),
end(v),
", ", lexcasts::hex() ) );
}
This example will print:
vector is: 1, 2, 3, 4, 5, 6
vector is: 0x1, 0x2, 0x3, 0x4, 0x5, 0x6
items greater than 3: 4, 5, 6
Helper function(s) for writing loops in declarative style. For example:
#include <cpp_util_3/loops.hpp>
using namespace cpp_util_3;
...
n_times( 3, []{ std::cout << "===" << std::endl; } );
Helper functions for creation of hex dumps of strings or byte buffers (could be useful for logging):
#include <cpp_util_3/hex_dumps/string_dumper.hpp>
#include <iostream>
using namespace std;
namespace dumps = cpp_util_3::hex_dumps;
int main()
{
// Will print '68 65 6c 6c 6f 2c 20 77 6f 72 6c 64':
cout << "'" << dumps::string_dumper("hello, world") << "'\n";
// Will print '68656c6c6f2c20776f726c64'
cout << "'" << dumps::string_dumper_without_spaces("hello, world") << "'\n";
}
Helper function do_with_rollback_on_exception
which simplifies
writting of exception safe code:
#include <cpp_util_3/rollback_on_exception.hpp>
...
void some_complex_container::insert_item( const some_data & item )
{
// Three internal containers must be updated.
// Any update can lead to exception.
// Container update must be rolled back on exception.
first_container_.push_back( item );
cpp_util_3::do_with_rollback_on_exception(
[&]{
second_container_.push_back( item );
cpp_util_3::do_with_rollback_on_exception(
[&]{ thrid_container_.push_back( itsm ); },
// Rollback action.
[&]{ second_container_.pop_back(); } );
},
// Rollback action.
[&]{ first_container_.pop_back(); } );
}
Helper class string_piece_t
is old and simple analog of string_span
from
C++ Core Guidelines or string_view
from proposals for C++17.
It can be used for functions and methods which except string-like params:
#include <cpp_util_3/string_piece.hpp>
...
bool is_valid_name( cpp_util_3::string_piece_t name ) {
... // Some manipulation of name's contents.
}
...
char name1[ 16 ] = {...};
if( is_valid_name( name1 ) ) { ... }
if( is_valid_name( "some name" ) ) { ... }
std::string name2 = ...;
if( is_valid_name( name2 ) ) { ... }