Skip to content

Commit

Permalink
support move-only handler types
Browse files Browse the repository at this point in the history
  • Loading branch information
arvidn committed Jun 28, 2017
1 parent 96e8e24 commit 84b5a41
Show file tree
Hide file tree
Showing 17 changed files with 351 additions and 205 deletions.
1 change: 1 addition & 0 deletions Jamfile
Expand Up @@ -87,6 +87,7 @@ lib simulator

: # usage requirements
<define>BOOST_ASIO_DISABLE_BOOST_DATE_TIME
<define>BOOST_ASIO_HAS_MOVE
<include>include
<link>shared:<define>SIMULATOR_LINKING_SHARED
<toolset>gcc:<cxxflags>-std=c++11
Expand Down
5 changes: 3 additions & 2 deletions appveyor.yml
@@ -1,12 +1,13 @@
version: 1.0.{build}
image: Visual Studio 2017
branches:
only:
- master
skip_tags: true
clone_depth: 1
install:
- set BUILD_DIR=%CD%
- set BOOST_ROOT=c:\Libraries\boost_1_59_0
- set BOOST_ROOT=c:\Libraries\boost_1_64_0
- set BOOST_BUILD_PATH=%BOOST_ROOT%\tools\build
- echo %BOOST_ROOT%
- echo %BOOST_BUILD_PATH%
Expand All @@ -19,7 +20,7 @@ install:
- cd %BUILD_DIR%
environment:
matrix:
- compiler: msvc-14.0
- compiler: msvc-14.1
- compiler: gcc
build_script:
- b2.exe --hash warnings-as-errors=on -j2 address-model=32 %compiler%
Expand Down
6 changes: 0 additions & 6 deletions include/simulator/config.hpp
Expand Up @@ -31,12 +31,6 @@ All rights reserved.
#define SIMULATOR_DECL
#endif

#if !defined _MSC_VER || _MSC_VER > 1900
#define LIBSIMULATOR_USE_MOVE 1
#else
#define LIBSIMULATOR_USE_MOVE 0
#endif

#if defined __clang__ || defined __GNUC__
#define LIBSIMULATOR_NO_RETURN __attribute((noreturn))
#elif _MSC_VER
Expand Down
143 changes: 143 additions & 0 deletions include/simulator/function.hpp
@@ -0,0 +1,143 @@
/*
Copyright (c) 2017, Arvid Norberg
All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef SIMULATOR_FUNCTION_HPP_INCLUDED
#define SIMULATOR_FUNCTION_HPP_INCLUDED

#include <utility>

namespace sim {
namespace aux {

// this is a std::function-like class that supports move-only function
// objects
template <typename R, typename... A>
struct callable
{
virtual R call(A&&...) = 0;
virtual ~callable() {}
};

template <typename Callable, typename R, typename... A>
struct function_impl : callable<R, A...>
{
function_impl(Callable c) : m_callable(std::move(c)) {}
R call(A&&... a) override
{
return m_callable(std::forward<A>(a)...);
}
private:
Callable m_callable;
};

template <typename Fun>
struct function;

template <typename R, typename... A>
struct function<R(A...)>
{
using result_type = R;

template <typename C>
function(C c)
: m_callable(new function_impl<C, R, A...>(std::move(c)))
{}
function(function&&) = default;
function& operator=(function&&) = default;

// boost.asio requires handlers to be copy-constructible, but it will move
// them, if they're movable. So we trick asio into accepting this handler.
// If it attempts to copy, it will cause a link error
function(function const&)
#ifndef _MSC_VER
;
#else
= default;
#endif
function& operator=(function const&) = delete;

function() = default;
explicit operator bool() const { return bool(m_callable); }
function& operator=(std::nullptr_t) { m_callable.reset(); return *this; }
void clear() { m_callable.reset(); }
R operator()(A... a)
{
assert(m_callable);
return m_callable->call(std::forward<A>(a)...);
}
private:
#ifdef _MSC_VER
// as of msvc-14.1, there's still terrible move support
std::shared_ptr<callable<R, A...>> m_callable;
#else
std::unique_ptr<callable<R, A...>> m_callable;
#endif
};

// index sequence, to unpack tuple
template<std::size_t...> struct seq {};
template<std::size_t N, std::size_t... S> struct gens : gens<N-1, N-1, S...> {};
template<std::size_t... S> struct gens<0, S...> { using type = seq<S...>; };

// a binder for move-only types, and movable arguments. It's not a general
// binder as it doesn't support partial application, it just binds all
// arguments and ignores any arguments passed to the call
template <typename Callable, typename R, typename... A>
struct move_binder
{
move_binder(Callable c, A... a)
: m_args(std::move(a)...)
, m_callable(std::move(c))
{}

move_binder(move_binder const&) = delete;
move_binder& operator=(move_binder const&) = delete;

move_binder(move_binder&&) = default;
move_binder& operator=(move_binder&&) = default;

// ignore any arguments passed in. This is used to ignore an error_code
// argument for instance
template <typename... Args>
R operator()(Args...)
{
return call(typename gens<sizeof...(A)>::type());
}

private:

template<std::size_t... I>
R call(seq<I...>)
{
return m_callable(std::move(std::get<I>(m_args))...);
}
std::tuple<A...> m_args;
Callable m_callable;
};

template <typename R, typename C, typename... A>
move_binder<C, R, A...> move_bind(C c, A... a)
{
return move_binder<C, R, A...>(std::move(c), std::forward<A>(a)...);
}

}
}

#endif

12 changes: 1 addition & 11 deletions include/simulator/packet.hpp
Expand Up @@ -37,12 +37,10 @@ namespace sim { namespace aux {
{}

// this is move-only
#if LIBSIMULATOR_USE_MOVE
packet(packet const&) = delete;
packet& operator=(packet const&) = delete;
packet(packet&&) = default;
packet& operator=(packet&&) = default;
#endif

// to keep things simple, don't drop ACKs or errors
bool ok_to_drop() const
Expand Down Expand Up @@ -70,11 +68,7 @@ namespace sim { namespace aux {
// used for UDP packets
// this is a unique_ptr just to make this type movable. the endpoint
// itself isn't
#if LIBSIMULATOR_USE_MOVE
std::unique_ptr<asio::ip::udp::endpoint> from;
#else
std::shared_ptr<asio::ip::udp::endpoint> from;
#endif

// the number of bytes of overhead for this packet. The total packet
// size is the number of bytes in the buffer + this number
Expand All @@ -97,11 +91,7 @@ namespace sim { namespace aux {

// this function must be called with this packet in case the packet is
// dropped.
#if LIBSIMULATOR_USE_MOVE
std::unique_ptr<std::function<void(aux::packet)>> drop_fun;
#else
std::shared_ptr<std::function<void(aux::packet)>> drop_fun;
#endif
aux::function<void(aux::packet)> drop_fun;
};

}} // sim
Expand Down
24 changes: 21 additions & 3 deletions include/simulator/queue.hpp
Expand Up @@ -28,8 +28,20 @@ All rights reserved.
#pragma warning( disable : 4251)
#endif

namespace sim
{
namespace sim {

struct timed_packet
{
timed_packet(chrono::high_resolution_clock::time_point t, aux::packet p)
: ts(t), pkt(std::move(p))
{}
timed_packet(timed_packet&&) = default;
timed_packet& operator=(timed_packet&&) = default;
timed_packet(timed_packet const&) = delete;
timed_packet& operator=(timed_packet const&) = delete;
chrono::high_resolution_clock::time_point ts;
aux::packet pkt;
};

// this is a queue. It can be configured to contrain
struct SIMULATOR_DECL queue : sink
Expand All @@ -42,6 +54,12 @@ namespace sim

virtual std::string label() const override final;

queue(queue const&) = delete;
queue& operator=(queue const&) = delete;

queue(queue&&) = default;
queue& operator=(queue&&) = default;

private:

void begin_send_next_packet();
Expand All @@ -65,7 +83,7 @@ namespace sim
std::string m_node_name;

// this is the queue of packets and the time each packet was enqueued
std::deque<std::pair<chrono::high_resolution_clock::time_point, aux::packet>> m_queue;
std::deque<timed_packet> m_queue;
asio::high_resolution_timer m_forward_timer;

chrono::high_resolution_clock::time_point m_last_forward;
Expand Down

0 comments on commit 84b5a41

Please sign in to comment.