Beyond C++ and the standard library, and before you dive into the massive Qt ecosystem, you can expand your C++ repertoire by familiarizing yourself with the Boost libraries. Now a fair chunk of this has been absorbed into C++11 standard library, so I've taken the time to identify the remaining Boost components that will provide added value to modern C++ dev, and will most likely osmosize their functionality into C++17 !
The Redundant Parts
If you are not using GCC or LLVM as your compiler, then you will most likely need to leverage these APIs to get modern C++ functionality:
-
Boost.Any
(replaced by std::auto)
-
Boost.Array
(replaced by std::array)
-
Boost.Atomic
(replaced by std::atomic)
-
Boost.Chrono
(replaced by std::chrono)
-
Boost.Concept_Check
(Replace by C++vnext Concepts)
-
Boost.Date_Time
(Replaced by C++11 std::chrono::)
-
Boost.Foreach
(Replaced by C++11 ranged for)
-
Boost.Function
(replaced by std::function)
-
Boost.Functional/Hash
(Replaced by std::hash<T>)
-
Boost.Heap
(Replaced by std::priority_queue)
-
Boost.Intrusive
(Replaced by STL Container move constructor / assignment)
-
Boost.Lambda
(Replaced by C++11 Lambdas)
-
Boost.Random
(Replaced by std::default_random_engine)
-
Boost.Ratio
2.0.0 (Replace with std::ratio)
-
Boost.Ref
(Replaced by std::ref)
-
Boost.Move
(Replaced with std::move)
-
Boost.StaticAssert
(Replaced by std::static_assert)
-
Boost
String Algorithms Library
-
Boost.Typeof
(Replaced by decltype)
-
Boost.Units
(Replaced by std::ratio constexpr)
-
Boost.Unordered
(Replaced by std::unordered_set / std::unordered_multiset)
-
Boost.Xpressive
(Replaced by std::regex_match)
-
Boost.TypeErasure
(Replaced
by C++14 auto lambdas)
Boost.Interprocess
using
namespace boost::interprocess;
Features:
-
mapped
memory regions - fixed-length memory buffers mapped as shared memory
or memory-mapped files
-
Semaphores,
mutexes, condition variables and upgradable mutexes – replaced by
std::memory
-
Message
queues.
Sharing
memory between processes – 3
mechanisms:
-
via
read/write file.
Requires manual synchronization.
-
via
OS
kernel
- eg
message
queues. synchronization is guaranteed by the kernel.
-
via
a shared
memory region - classical shared memory or memory mapped files.
Requires manual synchronization. FASTEST!
leverages
Linux: POSIX for native IPC , file permissions for synchronization /
shared memory mode. OS maps a memory segment in the address space of
several processes – they can read / write in that memory segment
without calling OS functions.
both
shared_memory_object
and file_mapping
objects can be used to create mapped_region
objects.
Limitations
When Constructing Objects In Mapped Regions -
References and
Virtuality
are
forbidden
Synchronization mechanisms:
managed_shared_memory
Mechanism:
allocate a portion of a shared memory segment. Create
named objects in it
- provide
a string key
so that other process can find, use and delete them from the
segment:
managed_shared_memory
segment(open_or_create,
"MySharedMemory",
intSize);
//
other options: create_only, open_only
void
*
shptr =
segment.allocate(2048);
//alloc
2048
bytes of raw
memory
managed_shared_memory::handle_t
handle =
segment.get_handle_from_address(shptr);
//
find / construct / find_or_construct
MyType
*p_o
=
segment.find_or_construct
<MyType>("MyInstance")
(/*
ctor params);
...
p_o
=
segment.find<MyType>("MyInstance");
segment.destroy<MyType>("MyInstance");
//Get
buffer local address from handle
void
*msg
=
segment.get_address_from_handle(handle);
segment.deallocate(msg);
...
//
validate free memory size as needed:
managed_shared_memory::size_type
free_memory_size
=
segment.get_free_memory();
Shared_memory_object
shared_memory_object
smo
(open_or_create
//other
options: open_only
/ create_only
,sName
,read_write
//other
options: read_only
/ write_only
);
smo.truncate(10000);
//
an
smo is created with size 0 – use truncate to set size
As
shared memory has kernel or filesystem persistence, the user must
explicitly destroy it via
remove:
//
map
all
/ part of
shared memory object (eg
obj) into
the process' address space using the mapped_region
class.
mapped_region
region(smo,
read_write /*mode*/,
smoSize,
regionSize
/*size_t*/);
region.get_address();
region.get_size();
shared_memory_object::remove(smo);
Anonymous
shared memory -
When processes are related via fork(),
a simpler method is available using anonymous shared memory, mapping
the device /dev/zero.
//Create
an anonymous shared memory segment with size 1000
mapped_region
region(anonymous_shared_memory(1000));
//Write
all the memory to 1
memset(region.get_address(),
1,
region.get_size());
file_mapping
memory
mapped files
-
associate
a file's contents with a one
or more mapped regions
of a
process
address space . Processes read / write to the file using pointers,
just like with dynamic memory. advantages:
-
Uniform
resource use.
-
Automatic
file data synchronization and cache from the OS.
-
Reuse
of C++ utilities (STL containers, algorithms) in files.
-
Shared
memory between two or more applications.
-
efficient
for large files – dont have to mapp whole file into memory
-
multiple
process' views point to identical copies of the file on disk.
OS
must synchronize file contents with memory --> not as fast an IPC
as shared memory.
file_mapping
m_file("/usr/home/file"
/*filename*/,read_write
/*mode*/);
Mapping
File's Contents to
all / part of Memory
of
process'
address space:
mapped_region
region(file,
read_write /*mode*/,
fileSize,
regionSize);
changes
are imediatelly visible to other processes. However, the file
contents on disk are not updated immediately, since that would hurt
performance - can manually flush a range from the view to disk:
region.flush(offset,
size);
offset_ptr<T>
a
smart pointer that
can
point safely to
objects
stored in the same shared memory segment
– eg
use
to create
a linked list in shared memory mapped
at different base addresses in different processes.
Mapping
Address In Several Processes - avoid
using raw pointers, use
offset_ptr
which
can
be safely placed in shared memory / memory mapped file.
mapped_region
region (
shm,
read_write
,
0
,
0
//Map
from offset 0 until the end
,
(void*)0x2D000000);
//Map
it exactly there
the
mapping address and the offset of the mappable object must
be
a multiple of the
page
size.
size_t
page_size =
mapped_region::get_page_size();
Message Queue
Threads
can enqueue
/ dequeue
messages in the priority
queue
using 3 methods:
-
Blocking:
If Q
is full when sending or is empty when receiving, the thread is
blocked until there is room for a new message or there is a new
message.
-
Try:
If Q
is full when sending or is empty when receiving, the thread returns
immediately with an error.
-
Timed:
If Q
is
full when sending or is empty when receiving, the thread retries the
operation until succeeds (returning successful state) or a timeout
is reached (returning a failure).
A
message queue
just copies raw bytes between processes and does not send objects.
The
object must be binary serializable. The
message queue is created with a name , a
maximum
message size and a
maximum message number.
message_queue
mq(open_or_create
,sQueueName
,nMaxMessages,
nMaxMessageSize);
for(int
i =
0;
i <
100;
++i){
mq.send(&i,
sizeof(i),
0);
...
//
in
2nd
process:
for(int
i =
0;
i <
100;
++i){
int
number;
mq.receive(&number,
sizeof(number),
recvd_size,
priority);
...
message_queue::remove(sQueueName);
//
explicit
removal
Boost.Lockfree
using
namespace boost::atomic;
using
namespace boost::lockfree;
Data
structure terminology
-
non-blocking
– implicitly
concurrent
user-space sync
via atomic ops for
optimal latency.do
not rely on kernel
locks and mutexes. Hardware
dependent
-
Wait-free
-
every
concurrent op is guaranteed to finish in a finite number of steps.
-
Lock-free
- some
concurrent ops are guaranteed to finish in a finite number of steps.
-
Obstruction-free
-
a concurrent op is guaranteed to finish in a finite number of steps,
unless another concurrent op interferes.
3
lock-free data structures that
intermediate between producer & consumer threads.
-
queue
- lock-free
multi-producer/multi-consumer
-
stack
- lock-free
multi-producer/multi-consumer
-
spsc_queue
- wait-free
single-producer/single-consumer (ringbuffer)
Data
Structures
are configured
with Boost.Parameter-style
templates: fixed_sized<int>
,
capacity<int>,
allocator<int>
queue<int>
queue(128);
queue.push(value);
...
queue.pop(value);
thread_group::create_thread(myMethod);
thread_group::join_all();
spsc_queue<int,
capacity<1024>
>
spsc_queue;
Boost.MPI
using
namespace boost::mpi;
using
namespace boost::mpi::threading;
HPC
inter-process communication via ptp or collective message
passing .
Boost
provides
a Python
(boost_mpi_python)
and C++
abstraction
to the
C
MPI standard
that
supports the creation, destruction, cloning, and splitting of MPI
communicators &
process groups. Need
to leverage an underlying
MPI implementation eg
Open
MPI, LAM/MPI, MPICH2. Note:
Allot
of
standard C MPI has
no
Boost.MPI equivalents.
An
MPI
program consists of many cooperating processes (possibly running on
different computers) that communicate among themselves by passing
messages over a communicator.
Each message has a source process, a target process (ranks
of the sender and receiver),
a tag, and a payload containing primitive or
user-defined data types &
functors.
#include
<boost/mpi/environment.hpp>
#include
<boost/mpi/communicator.hpp>
mpi::environment
env;
//
initializes the MPI environment and enables communication
mpi::communicator
comm;
//
assigns
a unique rank id to each process
//
& supports querying total number
cout
<<
"id"
<<
comm.rank()
<<
"
out
of
"
<<
comm.size()
<<
endl;
Point-to-point
ops
send
and receive blocking
/ non-blocking ops
//
sync blocking
string
msg;
if
(comm.rank()
==
0)
{
comm.send(1,
0,
string("Hello"));
comm.recv(1,
1,
msg);
cout
<<
msg <<
"!"
<<
endl;
}
else
{
comm.recv(0,
0,
msg);
cout
<<
msg <<
",
";
cout.flush();
comm.send(0,
1,
string("comm"));
}
//
assync
– prefix i
request
reqs[2];
if
(comm.rank()
==
0)
{
reqs[0]
=
comm.isend(1,
0,
msg);
reqs[1]
=
comm.irecv(1,
1,
msg);
wait_all(reqs,
reqs +
2);
}
else
{
reqs[0]
=
comm.isend(0,
1,
out_msg);
reqs[1]
=
comm.irecv(0,
0,
msg);
wait_all(reqs,
reqs +
2);
}
user
defined datatypes must be serializable - see type trait
is_mpi_datatype<T>
friend
class
boost::serialization::access;
template<class
Archive>
void
serialize(Archive
&
ar,
const
unsigned
int
version){
ar
&
xxx;
...
}
Collective ops
Broadcast
if
(comm.rank()
==
0)
{
broadcast(comm,
msg,
0);
//
send
to all
Gather
- collect values produced by every process in a communicator into a
vector of values on the master
process.
reduce
– use
a functor to
aggregate
values
from each process into a single value on
the master
process – like
STL
accumulate.
if
(comm.rank()
==
0)
{
vector<int>
all_numbers;
int
average;
gather(comm,
rand(),
all_numbers,
0);
//
gather
random numbers from each
reduce(comm,
rand(),
average,
mpi::average<int>(),
0);
}
else
{
gather(comm,
rand(),
0);
reduce(comm,
rand(),
mpi::average<int>(),
0);
}
ops
have
"all" variant all_reduce
/ all_gather
- performs the operation and broadcasts the result to all processes.
Managing communicators
Other
communicator “subnets”
can be constructed by splitting.
bool
is_generator =
comm.rank()
<
2
*
comm.size()
/
3;
communicator
local =
comm.split(is_generator?
0
:
1);
if
(is_generator)
generate_data(local,
comm);
else
collect_data(local,
comm);
probe
a message's rank as input into control flow decisions messaging:
status
msg =
comm.probe();
if
(msg.tag()
==
msg_data_packet)
{
comm.recv(msg.source(),
msg.tag(),
data);
group
provides facilities to compute the union (|),
intersection (&),
and difference (-)
of two groups, generate arbitrary subgroups, etc.
Boost.PropertyTree
provides
an arbitrary tree data structure of indexed KVP nodes, with easy
concatenated path access (eg level1.level2.level3) and various data
format representations (XML, INI, JSON). suited for holding
configuration data:
using
namespace boost::property_tree;
vector<string>
grandchildren
= { "aaa", "bbb", "ccc" };
ptree
pt1, pt2;
stringstream
ss;
pt1.put("parent.child1",
"xxx");
pt1.put("parent.child2",
5.0);
for
( const auto &v : grandchildren)
{
ptree
pt3;
pt3.put("e1",
v);
pt1.add_child("parent.grandchildren.grandchild",
pt3);
}
write_xml(ss,
pt1);
read_xml(ss,
pt2);
cout
<< pt2.get<string>("parent.child1") <<
endl;
cout
<< pt2.get("parent.child2", 0) << endl;
for
(const auto &v : pt2.get_child("parent.grandchildren"))
{
cout
<< v.second.data() << endl;
}
Boost.Proto
An
API for constructing expression trees for embedding DSLs in C++.
namespaces:
boost::mpl;
boost::fusion;boost::proto;
Entity
|
Example
|
Free
Function
|
boost::proto::value()
|
Metafunction
|
boost::proto::result_of::value<>
|
Function
Object
|
boost::proto::functional::value
|
Transform
|
boost::proto::_value
|
I
found this to be too esetoric to dive into.
Boost.Signals2
using
namespace boost::signals2;
A
pubsub system of
signals and slots . Signals are
publishers -
callbacks with multiple targets, connected to slots (
subscribers ie
callback receivers),
which are called when the event
is published.
Managed
- track signal/slot connections, manage their lifetimes and
automatically disconnect connections when either is destroyed.
Signals2
-
thread-safe , automatic connection management.
Calling
slots
struct
MyFunctor{
void
operator()() const {
cout
<< "blah" << endl;
}
};
signal<void
()> sig; // Signal with
no args or retval
MyFunctor
f;
sig.connect(f());
// Connect a functor
slot to the signal
sig();
// Call all of the slots
Calling
multiple slots - Can
connect
multiple
slots
and
order
slot
call
groups
in
the slot list
sig.connect(1,
f()); // connect with group 1
sig.connect(0,
g()); // connect with group 0
unnamed
slots (i.e., those connected without specifying a group name) can be
placed at the front or back of the slot list (by passing at_front
or at_back
as the last parameter to connect),
and default to the end of the list.
slots
are invoked in order:1) ungrouped slots connected with at_front;
2) grouped slots according to ordering of their groups;3) ungrouped
slots connected with at_back
Passing Values to and from Slots
Eg
a Slot with 2 Args and a retval
float
product(float x, float y) { return x * y; }
signal<float
(float, float)> sig;
sig.connect(&product);
float
result = *sig(5., 10.);
Signal
Return Values - values
can be returned back to the caller of the signal through an
optional
combiner: The
default combiner returns a boost::optional
containing the return value of the last slot in the slot list.Can
create a custom combiner functor
and pass it to the signal constructor template – can return a
scalar, a vector, apply filtering etc.
signal<float
(float x, float y), maximum<float>
> sig;
Slot
functors
can be passed via slot_type
(IMHO:
use decltype)
typedef
signal<void
(int x, int y)> OnClick;
typedef
OnClick::slot_type
OnClickSlotType;
//
forward slots through Button
interface to its private signal
connection
doOnClick(const OnClickSlotType &
slot);
Connection Lifetime Management
connection
class represents the connection between a particular signal and a
particular slot. The connected()
method checks if the signal and slot are still connected, and the
disconnect()
method disconnects the signal and slot if they are connected before
it is called.
connection
c = sig.connect(f());
sig();
c.disconnect();
sig();
// Does nothing: there are no
connected slots
Blocking
Slots - shared_connection_block
object will temporarily block a slot ignored when the signal is
invoked (egused
to prevent infinite recursion)
. The connection is unblocked by either destroying or calling
unblock:
connection
c = sig.connect(f());
{
boost::signals2::shared_connection_block
block(c); // block
the slot
}
// block going out of scope
unblocks the slot
scoped_connection
object
-
a signal/slot connection that will be disconnected when the
scoped_connection
class goes out of scope. For
temporary connections:
{
scoped_connection
c(sig.connect(ShortLived()));
sig();
// will call ShortLived functor
}
goes out of scope and disconnects
use
disconnect
method to
disconnect specific slots by functor instance:
sig.connect(&f);
sig.connect(&g);
sig();
sig.disconnect(&foo); //
disconnects f but not g
Automatic
Connection Management -
automatic
disconnection of slots occurs
when
objects involved in the slot call are destroyed - track any object
which is managed by a shared_ptr, by using slot::track_foreign.
no explicit call to bind()
needed for
object params
– its
implicit.
shared_ptr<MyFunctor>
pf(new MyFunctor());
sig
.connect(signal_type::slot_type(&pf,pf.get(),_1)
.track_foreign(newsMessageArea));
Signal/slot
disconnections occur when:
-
explicit
disconnection
via signal::disconnect
/
connection::disconnect
-
scoped_connection's
destructor.
-
An
object tracked by the slot is destroyed.
-
The
signal is destroyed.
to
disconnect or block a slot's connection from within the slot itself,
signal::connect_extended
methods allow slots which take an extra connection
argument
to be connected to a signal.
Changing
the Mutex
Type of a Signal -
to use an alternate mutex type to
the default boost::signals2::mutex,
it must be default-constructible and fulfill the Lockable
concept defined by the
Boost.Thread
library (it
must have lock()
and
unlock()
methods). Boost
provides an
alternate mutex class dummy_mutex
- a
fake for use in single-threaded programs, where locking a real mutex
would be useless overhead.
Boost.Asio
using
namespace boost::asio;
using
namespace
boost::asio::ip;
perform
synchronous / asynchronous operations on I/O objects such as
sockets.
io_service
object – socket
uses this as an interface
to the underlying
OS
I/O services.
io_service
io_service;
error_code
ec;
socket.connect(server_endpoint,
ec);
or
socket.async_connect(server_endpoint,
my_completion_handler);
..
void
my_completion_handler(const
error_code&
ec);
call
io_service::run()
for result to be retrieved-
blocks while there are unfinished asynchronous operations. This
is thread-safe.io_service::post()
supports threadpools.
Boost.Asio
implements the
Proactor Design Pattern (concurrency
without
threads
http://en.wikipedia.org/wiki/Proactor_pattern
)
Strands:
Use Threads Without Explicit Locking - io_service::strand::wrap()
-
for
sequential
invocation of event handler functors
-->
avoid
explicit mutex
locking.
Buffers
mutable_buffer
and
const_buffer
provide
protection against buffer overruns and
type safety via buffer_cast
support
for scatter-gather operations:A scatter-read receives data into
multiple buffers, and
a
gather-write transmits multiple buffers via
buffer
containers
MutableBufferSequence
and ConstBufferSequence.
basic_streambuf
provides
iostream integration - data()
exposes
input sequence and
prepare()
exposes
output
sequence. commit()
publishes
data and
consume()
subscribes
it.
Streams,
Short Reads and Short Writes
sync
/ async stream support:: read_some(),
async_read_some(),
write_some().
stream-oriented
I/O objects include ip::tcp::socket,
ssl::stream<>,
posix::stream_descriptor.
See:
async_read(),
async_write(),
read(),
write(),
AsyncReadStream,
AsyncWriteStream,
SyncReadStream,
SyncWriteStream.
Operations
Reactor-Style Ops
- 3rd
party integration - See
null_buffers,
basic_socket::non_blocking(),
basic_socket::native_non_blocking()
Line-Based
Ops - Many
internet
application
protocols
(eg
HTTP, FTP)
are
line-based
- char
delimited by "\r\n".
For
this, use
read_until()
/ async_read_until().
using
namespace
boost;
using
namespace
boost::system;
tcp::socket
socket_;
streambuf
data_;
//store
data read from socket
async_read_until(socket_,
data_,
"\r\n",
boost::bind(&http_connection::handle_request_line,
this,
_1));
void
handle_request_line(error_code
ec){
if
(!ec)
istream
is(&data_);
The
delimiters may be specified as a single char,
a string
or a boost::regex.
The functions also include overloads that accept a functor
that matches via the is_match_condition<>
type
trait
streambuf
b;
read_until(s,
b,
match_char('a'));
Custom Memory
Allocation - If
a protocol handler requires
allocating
temp memory
, use:
void*
asio_handler_allocate(size_t,
...);
void
asio_handler_deallocate(void*,
size_t,
...);
Handler
Tracking - for
debugging asynchronous programs, define
BOOST_ASIO_ENABLE_HANDLER_TRACKING
symbol
for stderr dumping.
Coroutines
coroutine
class - implement asynchronous logic in a synchronous manner, with
minimal overhead (ie
stackless):
struct
session :
coroutine
{
shared_ptr<tcp::socket>
socket_;
shared_ptr<vector<char>
>
buffer_;
session(shared_ptr<socket>
socket)
:
socket_(socket),
buffer_(new
vector<char>(1024))
{}
void
operator()(error_code
ec =
error_code(),
size_t n =
0){
if
(!ec)
reenter
(this){
for
(;;){
yield
socket_->async_read_some(buffer(*buffer_),
*this);
yield
async_write(*socket_,
buffer(*buffer_,
n),
*this);
used
in conjunction with the pseudo-keywords reenter,
yield
and
fork,
which
are preprocessor macros.
The
spawn()
function is a high-level wrapper for running stackful coroutines:
spawn(my_strand,
do_echo);
void
do_echo(yield_context
yield){
char
data[128];
for
(;;){
size_t
length =
my_socket.async_read_some(buffer(data),
yield);
async_write(my_socket,
buffer(data,
length),
yield);
TCP, UDP && ICMP
Boost.Asio
provides off-the-shelf support for the internet protocols TCP, UDP
and ICMP.
TCP
Clients - Hostname
resolution is performed using a resolver:
resolver
resolver(my_io_service);
resolver::query
query("www.boost.org",
"http");
resolver::iterator
iter =
resolver.resolve(query);
resolver::iterator
end;
//
End marker.
while
(iter
!=
end){
endpoint
endpoint =
*iter++;
TCP
clients establish connections using connect()
and async_connect():
socket
socket(my_io_service);
connect(socket,
resolver.resolve(query));
//
or asynchronous connect:
async_connect(socket_,iter,
bind(&client::handle_connect,
this,
placeholders::error));
...
void
handle_connect(const
error_code&
error){
When
a specific endpoint is available, a socket can be created and
connected:
socket.connect(endpoint);
Data
may be read from or written to a connected TCP socket using: read(),
async_read(),
write()
and async_write().
TCP
Servers - use an acceptor
acceptor
acceptor(my_io_service,
my_endpoint);
socket
socket(my_io_service);
acceptor.accept(socket);
UDP
- hostname resolution is also performed using a resolver, typically
bound to a local endpoint. The following
code will create an IP version 4 UDP socket and bind it to the "any"
address on port 12345:
ip::udp::endpoint
endpoint(ip::udp::v4(),
12345);
ip::icmp::endpoint
endpoint(ip::icmp::v6(),
0);
The
port number is not used for ICMP.
For
a connected UDP socket, use the receive(),
async_receive(),
send()
or async_send()
m; for
unconnected ICMP socket use
receive_from(),
async_receive_from(),
send_to()
or async_send_to()
Support
for other socket protocols - (such as Bluetooth or IRCOMM sockets)
can be added by deriving
from one of these
classes:
generic::datagram_protocol
generic::raw_protocol
generic::seq_packet_protocol
generic::stream_protocol
Socket Iostreams
iostreams
are abstracted on top of sockets - protocol independence, etc.:
ip::tcp::iostream
stream("www.boost.org",
"http");
can
be used with an acceptor to create simple servers:
io_service
ios;
tcp::endpoint
endpoint(tcp::v4(),
80);
tcp::acceptor
acceptor(ios,
endpoint);
for
(;;){
ip::tcp::iostream
stream;
acceptor.accept(*stream.rdbuf());
Timeouts
may be set by calling expires_at()
or
expires_from_now():
ip::tcp::iostream
stream;
stream.expires_from_now(boost::posix_time::seconds(60));
stream.connect("www.boost.org",
"http");
stream
<<
"blah\r\n";
stream.flush();
cout
<<
stream.rdbuf();
will
fail if all the socket operations combined take longer than 60
seconds.
If
an error does occur, the iostream's error()
member function may be used to retrieve the error code from the most
recent system call:
if
(!stream){
cout
<<
"Error:
"
<<
stream.error().message()
<<
"\n";
See
ip::tcp::iostream,
basic_socket_iostream,
iostreams
examples.
BSD Sockets
The
Boost.Asio library includes a low-level socket interface based on the
BSD socket API:
Timers
eg
perform a synchronous wait operation on a timer using a relative
time:
io_service
i;
...
deadline_timer
t(i);
t.expires_from_now(seconds(5));
t.wait();
eg
perform an asynchronous wait operation on a timer:
void
handler(error_code
ec)
{
...
}
...
io_service
i;
...
deadline_timer
t(i);
t.expires_from_now(milliseconds(400));
t.async_wait(handler);
...
i.run();
The
deadline associated with a timer may also be obtained as a relative
time:
time_duration
time_until_expiry =
t.expires_from_now();
or
as an absolute time to allow composition of timers:
t2.expires_at(t.expires_at()
+
seconds(30));
Serial Ports
a
serial port may be opened using:
serial_port
port(my_io_service,
"/dev/ttyS0");
Once
opened, the serial port may be used as a stream.
Signal Handling
Add
signals to one or more
signal_set
objects,
then
perform an async_wait()
operation.
//
Construct a signal set registered for process termination.
signal_set
signals(io_service,
SIGINT,
SIGTERM);
//
Start an asynchronous wait for one of the signals to occur.
signals.async_wait(handler);
UNIX Domain Sockets
- Boost.Asio
provides basic support UNIX domain sockets
See:
local::connect_pair,
local::datagram_protocol,
local::datagram_protocol::endpoint,
local::datagram_protocol::socket,
local::stream_protocol,
local::stream_protocol::acceptor,
local::stream_protocol::endpoint,
local::stream_protocol::iostream,
local::stream_protocol::socket,
UNIX
domain sockets examples.
Stream-Oriented File Descriptors
Boost.Asio
includes classes added to permit synchronous and asynchronous read
and write operations to be performed on POSIX file descriptors, such
as pipes, standard IO,
and various devices (but not regular files).
See
posix::stream_descriptor,
posix::basic_stream_descriptor,
posix::stream_descriptor_service
Fork
Boost.Asio
supports process
forking and parent child notifications:
io_service_.notify_fork(fork_prepare);
if
(fork()
==
0){
io_service_.notify_fork(fork_child);
...
}
else {
io_service_.notify_fork(fork_parent);
...
}
SSL
can
layer encrypted comms over an existing stream, such as a TCP socket.
ssl::context
ctx(ssl::context::sslv23);
ctx.set_verify_mode(ssl::verify_peer);
ctx.load_verify_file("ca.pem");
To
use SSL with a TCP socket, one may write:
ssl::stream<tcp::socket>
ssl_sock(my_io_service,
ctx);
To
perform socket-specific operations, such as establishing an outbound
connection or accepting an incoming one, the underlying socket must
first be obtained:
tcp::socket::lowest_layer_type&
sock =
ssl_sock.lowest_layer();
sock.connect(my_endpoint);
for
configuring SSL certificate verifiication:
ssl::context::set_default_verify_paths()
ssl::context::set_verify_mode()
ssl::context::set_verify_callback()
ssl::context::load_verify_file()
ssl::stream::set_verify_mode()
ssl::stream::set_verify_callback()
Timers
Boost.Asio
provides chrono
timers
via the basic_waitable_timer
class template. The typedefs system_timer,
steady_timer
and high_resolution_timer
utilise the standard clocks system_clock,
steady_clock
and high_resolution_clock
respectively.
Boost.Accumulators
Framework
for performing
statistical computation and
incremental calculations. An
accumulator
is
a
concrete
primitive computational entity. Instantiate
an accumulator_set<>,
specifying
built
in / custom
computations, push
data
in one sample at a time -->
it will calculate
the computations
specified
accumulator_set<double,
stats<tag::mean,
tag::varience
>
>
acc;
//
push in some data ...
acc(1.1);
acc(2.2);
acc(3.3);
//
Display the results ...
cout
<<
"Varience:
"
<<
varience(acc)
<<
endl;
API
|
|
accumulator_set<>
|
datum
pushed in
is
forwarded to each accumulator in
the set
|
depends_on<>
|
specify
which other features a feature depends on.
|
feature_of<>
|
one
feature should be treated the same as another.
|
as_feature<>
|
create
an alias for a feature
|
features<>
|
An
MPL
sequence
|
external<>
|
store
weight accumulators in a separate accumulator set
|
extractor<>
|
functor
used to extract a result
|
template<
typename
Sample,
typename
Features,
typename
Weight =
void
>
struct
accumulator_set;
template
parameters:
-
Sample
-
type of the data that will be accumulated.
-
Features
- An
MPL
sequence of features to be calculated. A
feature is an abstract primitive computational entity.
-
Weight-The
type of the (optional) weight scalar
paramter.
Extracting
Results -
For each added
feature from
boost::accumulators
,
there is a corresponding extractor with
same name for
fetching its result in boost::accumulators::extract
namespace.
Can
also extract explicitly
with extract_result()
can
define custom
extractors
using the extractor<>
class template.
Passing
Optional Parameters -
with named parameters from the Boost.Parameter
library.
Weighted
Samples
- Each sample pushed into the accumulator has an associated weight,
by which the sample is conceptually multiplied. specify the type of
the weight parameter as the 3rd template parameter
-->
all the accumulators in the set are replaced with their weighted
equivalents.
Defining
a New Accumulator - inherit from
accumulator_base
and
satisfy the Accumulator
Concept.
Droppable
Accumulators -
an accumulator that can be removed from the accumulator_set<>
using the droppable<>
class template.
Defining
a New Feature - inherit from
depends_on<> to specify
dependencies, satisfy
the Feature
Concept.Define
a nested typedef called 'impl' that specifies which accumulator
implements this feature.The
nested impl
type must be an MPL
Lambda Expression.
-->
use of MPL
placeholders like mpl::_1
Aliasing
feature dependencies with
feature_of<>
- inform
the Accumulators Framework that two features are the same from the
point of view of dependency resolution
Register
feature variants with
as_feature<>
Miscalaneous Boost Features
Boost.Container
-
Move
semantics emulation for pre-C++11 compilers.
-
placement
insertion, recursive containers
-
compatible
with Boost.Interprocess
( can be safely placed in shared memory)
-
allocator_traits
is the protocol between a container and an allocator – level
of abstraction for stateful allocators
-
C++11
stateful allocators - scoped_allocator_adaptor
class
Boost.Circular Buffer (Like Disruptor)
boost::circular_buffer
is a STL compliant container (eg push_back)
There
is also a circular_buffer_space_optimized
version available.
circular_buffer<int>
cb(10);
//
buffer has
capacity for 10
integers.
like
other STL
implementations,the circular_buffer
is not fully thread-safe.
The
circular_buffer
should not be used for storing pointers to dynamically allocated
objects -->
memory
leaks.
Numeric Operators Sub-Library
boost::numeric
namespaceprovides
function objects and meta-functions corresponding to the infix
operators in C++.
boost::numeric::operators
namespace are additional operator overloads for some useful
operations not provided by the standard library
In
the boost::numeric::functional
namespace are function object equivalents of the infix operators.
For
example, boost::numeric::plus(a,
b)
is
equivalent to
boost::numeric::functional::plus<A,
B>()(a,
b),
and both are equivalent to using
namespace
boost::numeric::operators;
a +
b;.
Boost.Tribool
boost::logic::tribool
supports
3
states:
true,
false,
&
indeterminate.
supports conversion from bool
values and literals:
tribool
b(true);
b
= false;
b
= indeterminate;
tribool
b2(b);
Boost.Variant
The
variant
class template is a safe, generic, stack-based discriminated union
container ie
"multi-type,
single value."
Full
value semantics, including adherence to standard overload resolution
rules for conversion operations.
Compile-time
type-safe value visitation via boost::apply_visitor.
Run-time
checked explicit value retrieval via boost::get.
Too
esetoric for me to investigate further right now :)
Boost.Lexical_Cast 1.0 (stringstream wrapper)
lexical_cast<T>(S
s)
evolution
from unsafe and archaic standard
C functions atoi/itoa
family,
strtol
, scanf
family
convenient
and consistent common conversions with
expression-level convenience - see
also
numeric_cast