I am finding it a bit silly to ask, to because the minimal example I am posting here works, but when I implement it as part of my project, I get a segfault. But I will still go ahead and ask.
Below is my code
#include <iostream>
#include <thread>
#include <boost/array.hpp>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
using boost::asio::ip::udp;
class Transport
{
public:
virtual bool connect() = 0;
virtual void disconnect() = 0;
virtual void read(boost::array<char, 4096> &buf, size_t &len) = 0;
Transport(std::string address) : address_(address) {}
void set_port(std::string port)
{
port_ = std::move(port);
}
std::string get_port()
{
return port_;
}
std::string get_host_ip()
{
return host_ip_;
}
protected:
std::string address_;
std::string host_ip_;
std::string port_;
boost::asio::io_service io_service_;
};
class TCPTransport : public Transport
{
public:
TCPTransport(std::string address) : Transport(address), socket_(io_service_) {}
~TCPTransport()
{
disconnect();
}
virtual bool connect()
{
try
{
std::cout << "io_service " << io_service_.stopped() << std::endl;
tcp::resolver resolver(io_service_);
tcp::resolver::query query(address_, port_);
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::resolver::iterator end;
boost::system::error_code error = boost::asio::error::host_not_found;
while (error && endpoint_iterator != end)
{
socket_.close();
socket_.connect(*endpoint_iterator++, error);
}
if (error)
{
throw boost::system::system_error(error);
}
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
return false;
}
return true;
}
virtual void disconnect()
{
std::cout << "disconnecting..." << std::endl;
socket_.close();
}
void read(boost::array<char, 4096> &buf, size_t &len)
{
boost::system::error_code error;
len = socket_.read_some(boost::asio::buffer(buf), error);
if (error == boost::asio::error::eof)
return; // Connection closed cleanly by peer.
else if (error)
throw boost::system::system_error(error); // Some other error.
}
private:
tcp::socket socket_;
};
class Writer
{
public:
Writer(std::shared_ptr<Transport> &transport) : transport_(transport) {}
void connect()
{
transport_->connect();
}
void start_read()
{
while(true)
{
size_t len;
boost::array<char, 4096> buf;
transport_->read(buf, len);
// std::cout.write(buf.data(), len);
std::cout << len << std::endl;
}
}
private:
std::shared_ptr<Transport> transport_;
};
class ConnectionHolder
{
public:
ConnectionHolder(std::shared_ptr<Transport> &transport) : transport_(transport) {}
void init()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
transport_->set_port("8000");
transport_->connect();
writer_ = std::make_shared<Writer>(transport_);
}
void start()
{
writer_->start_read();
}
private:
std::shared_ptr<Transport> transport_;
std::shared_ptr<Writer> writer_;
};
int main(int argc, char* argv[])
{
if (argc != 3)
{
std::cerr << "Usage: client <host>" << std::endl;
return 1;
}
std::shared_ptr<Transport> transport = std::make_shared<TCPTransport>(argv[1]);
ConnectionHolder holder(transport);
// transport->set_port(argv[2]);
holder.init();
holder.start();
return 0;
}
Before I create a TCP socket, I need to request for a TCP handle. Once done, I can start reading from the device. The code attached above works well. But in case of my actual code, valgrind complains with:
==18714== Invalid read of size 8
==18714== at 0x4C367E0: memmove (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==18714== by 0x12EDF1: boost::asio::ip::basic_resolver_iterator<boost::asio::ip::tcp>::create(addrinfo*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (basic_resolver_iterator.hpp:99)
==18714== by 0x12C657: boost::asio::detail::resolver_service<boost::asio::ip::tcp>::resolve(std::shared_ptr<void>&, boost::asio::ip::basic_resolver_query<boost::asio::ip::tcp> const&, boost::system::error_code&) (resolver_service.hpp:69)
==18714== by 0x12989F: boost::asio::ip::resolver_service<boost::asio::ip::tcp>::resolve(std::shared_ptr<void>&, boost::asio::ip::basic_resolver_query<boost::asio::ip::tcp> const&, boost::system::error_code&) (resolver_service.hpp:113)
==18714== by 0x126A8B: boost::asio::ip::basic_resolver<boost::asio::ip::tcp, boost::asio::ip::resolver_service<boost::asio::ip::tcp> >::resolve(boost::asio::ip::basic_resolver_query<boost::asio::ip::tcp> const&) (basic_resolver.hpp:102)
==18714== by 0x12290D: TCPTransport::connect() (communication.h:99)
==18714== by 0x137E3D: Interface::start_transmission() (pf_interface.cpp:94)
==18714== by 0x117843: main (main.cpp:36)
==18714== Address 0x10fe3fc000000000 is not stack'd, malloc'd or (recently) free'd
==18714==
==18714==
==18714== Process terminating with default action of signal 11 (SIGSEGV)
==18714== General Protection Fault
==18714== at 0x4C367E0: memmove (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==18714== by 0x12EDF1: boost::asio::ip::basic_resolver_iterator<boost::asio::ip::tcp>::create(addrinfo*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (basic_resolver_iterator.hpp:99)
==18714== by 0x12C657: boost::asio::detail::resolver_service<boost::asio::ip::tcp>::resolve(std::shared_ptr<void>&, boost::asio::ip::basic_resolver_query<boost::asio::ip::tcp> const&, boost::system::error_code&) (resolver_service.hpp:69)
==18714== by 0x12989F: boost::asio::ip::resolver_service<boost::asio::ip::tcp>::resolve(std::shared_ptr<void>&, boost::asio::ip::basic_resolver_query<boost::asio::ip::tcp> const&, boost::system::error_code&) (resolver_service.hpp:113)
==18714== by 0x126A8B: boost::asio::ip::basic_resolver<boost::asio::ip::tcp, boost::asio::ip::resolver_service<boost::asio::ip::tcp> >::resolve(boost::asio::ip::basic_resolver_query<boost::asio::ip::tcp> const&) (basic_resolver.hpp:102)
==18714== by 0x12290D: TCPTransport::connect() (communication.h:99)
==18714== by 0x137E3D: Interface::start_transmission() (pf_interface.cpp:94)
==18714== by 0x117843: main (main.cpp:36)
--18714-- Discarding syms at 0x112cb350-0x112d1d7f in /lib/x86_64-linux-gnu/libnss_files-2.27.so due to munmap()
Does this mean that io_service object doesn't exist anymore by the time I call connect? I don't get it, because I am using a shared_ptr. The transport.h is exactly the same, just that instead of main.cpp, I call connect from another class.
Edit 1: I added the following line in constructor of Transport.
io_service_thread_ = boost::thread(boost::bind(&boost::asio::io_service::run, &io_service_));
But I get the same error!
You need to run io_context/io_service loop with io_service.run(). It starts the IO loop to keep the objects alive during the time main() runs.
You can create io_service instance in the main and pass a reference to it around the other classes like Transport and they can maintain a reference to the io_service object.
Once your communication objects are all setup, you can call io_service.run() in the main().
Or else, you can use your Transport class to run the service loop as well.
Related
I was using boost version boost 1_64_0 to run a Tcp server in a seprate thread
This was the code for the server.
https://www.boost.org/doc/libs/1_52_0/doc/html/boost_asio/example/echo/async_tcp_echo_server.cpp
in the Main loop i could spawn a new thread like this.
int main()
{
// Run the boost echo server as a different thread
boost::asio::io_service io_service;
server server1(io_service, 1980);
boost::thread t(boost::bind(&io_service::run, &io_service));
// when about to close the program
io_service.stop(); // stop the server
t.join();
}
Now i have changed the boost version to boost_1_73_0 and i am using this example to create a server
https://www.boost.org/doc/libs/1_71_0/doc/html/boost_asio/example/cpp11/echo/async_tcp_echo_server.cpp
how do to create a new thread ?
The existing code to create a new thread gives error.
io_service: left of :: must be a class/struct/union
run : undeclared identifier
In &io_service::run, io_service is not a type but the local variable by that name.
Starting from https://www.boost.org/doc/libs/1_74_0/doc/html/boost_asio/example/cpp11/echo/async_tcp_echo_server.cpp
io_context.run();
Needs to be like
std::thread t([&] { io_context.run(); });
// ...
t.join();
Or if you insist:
std::thread t(boost::bind(&boost::asio::io_context::run, &io_context));
I would use a thread_pool to shortcut all the complexities¹:
boost::asio::thread_pool io(1);
server s(io.get_executor(), std::atoi(argv[1]));
// ...
io.join();
A trivial edit to the class interface is required (see e.g. Boost 1.70 io_service deprecation)
Live On Coliru
//
// async_tcp_echo_server.cpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include <boost/asio.hpp>
#include <boost/bind/bind.hpp>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
using boost::asio::ip::tcp;
class session : public std::enable_shared_from_this<session> {
public:
session(tcp::socket socket) : socket_(std::move(socket)) {}
void start() { do_read(); }
private:
void do_read() {
auto self(shared_from_this());
socket_.async_read_some(
boost::asio::buffer(data_, max_length),
[this, self](boost::system::error_code ec, std::size_t length) {
if (!ec) {
do_write(length);
}
});
}
void do_write(std::size_t length) {
auto self(shared_from_this());
boost::asio::async_write(
socket_, boost::asio::buffer(data_, length),
[this, self](boost::system::error_code ec, std::size_t /*length*/) {
if (!ec) {
do_read();
}
});
}
tcp::socket socket_;
enum { max_length = 1024 };
char data_[max_length];
};
class server {
public:
template <typename Executor>
server(Executor ex, short port)
: acceptor_(ex, tcp::endpoint(tcp::v4(), port)) {
do_accept();
}
private:
void do_accept() {
acceptor_.async_accept(
[this](boost::system::error_code ec, tcp::socket socket) {
if (!ec) {
std::make_shared<session>(std::move(socket))->start();
}
do_accept();
});
}
tcp::acceptor acceptor_;
};
int main(int argc, char* argv[]) {
try {
if (argc != 2) {
std::cerr << "Usage: async_tcp_echo_server <port>\n";
return 1;
}
boost::asio::thread_pool io(1);
server s(io.get_executor(), std::atoi(argv[1]));
// ...
io.join();
} catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
}
}
¹ see e.g. Should the exception thrown by boost::asio::io_service::run() be caught?
I've defined a guard in a sub-state machine and now I want to use the exact logic/guard in the parent SM (using boost-msm). using the same guard in the transition_table results in compilation error: unknown type name 'GuardSS'.
I've defined the guard (and its corresponding value) in the sub-machine, but it can be moved to the parent SM if it helps in solving the problem and allows me to reuse the guard.
A sample code can be found here.
How can I reuse the code used in the guard?
Code included:
#include <iostream>
#include "myfsm.h"
int main()
{
std::cout << "Testing boost::msm ..." << std::endl;
MyFsm fsm;
fsm.start();
fsm.process_event(Event12());
fsm.process_event(Event_ss12());
//guard valu changes
MyFsm_::State2 &state = fsm.get_state<MyFsm_::State2&>();
state.m_guardVal = true;
fsm.process_event(Event_ss12());
fsm.process_event(Event21());//?protect this transition using the same Guard
}
fsm.h:
#ifndef MYFSM
#define MYFSM
#include <iostream>
#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>
struct Event12{};
struct Event21{};
struct Event_ss12{};
struct Event_ss21{};
namespace msm = boost::msm;
namespace msmf = boost::msm::front;
namespace mpl = boost::mpl;
struct MyFsm_ : msmf::state_machine_def<MyFsm_>
{
struct State1 : msmf::state<>{
template<class Event, class Fsm> void on_entry(const Event&, Fsm&) const {std::cout << "State1::on_entry()" << std::endl;}
template<class Event, class Fsm> void on_exit(const Event&, Fsm&) const {std::cout << "State1::on_exit()" << std::endl;}
};
struct State2_ : msmf::state_machine_def<State2_>{
template <class Event,class Fsm> void on_entry(Event const&, Fsm&) const {std::cout << "State2::on_entry()" << std::endl;}
template <class Event,class Fsm> void on_exit(Event const&, Fsm&) const {std::cout << "State1::on_exit()" << std::endl;}
struct SubState1 : msmf::state<> {
template <class Event,class Fsm> void on_entry(Event const&, Fsm&) const {std::cout << "SubState1::on_entry()" << std::endl;}
template <class Event,class Fsm> void on_exit(Event const&, Fsm&) const {std::cout << "SubState1::on_exit()" << std::endl;}
};
struct SubState2 : msmf::state<> {
template <class Event,class Fsm> void on_entry(Event const&, Fsm&) const {std::cout << "SubState2::on_entry()" << std::endl;}
template <class Event,class Fsm> void on_exit(Event const&, Fsm&) const {std::cout << "SubState2::on_exit()" << std::endl;}
};
// Guards
struct GuardSS {
template <class Event, class Fsm, class SourceState, class TargetState>
bool operator()(Event const&, Fsm& fsm, SourceState&, TargetState&)
{
if (fsm.m_guardVal == true){
std::cout << "Transition Approved by Guard \n";
return true;
}
std::cout << "Transition Rejected by Guard \n";
return false;
}
};
typedef SubState1 initial_state;
struct transition_table:mpl::vector<
// Start Event Next Action Guard
msmf::Row < SubState1, Event_ss12, SubState2, msmf::none, GuardSS >,
msmf::Row < SubState2, Event_ss21, SubState1, msmf::none, msmf::none >
> {};
bool m_guardVal=false;
};
typedef msm::back::state_machine<State2_> State2;
// Set initial state
typedef State1 initial_state;
// Transition table
struct transition_table:mpl::vector<
msmf::Row < State1, Event12, State2, msmf::none, msmf::none >,
msmf::Row < State2, Event21, State1, msmf::none, GuardSS/*msmf::none*/ >
>{};
template<class Event, class Fsm>
void no_transition(Event const&, Fsm&, int state){
std::cout<<"no_transiton detected from state: "<< state << std::endl;
}
};
// Pick a back-end
typedef msm::back::state_machine<MyFsm_> MyFsm;
#endif // MYFSM
You need to move the guard GuardSS to the parent state MyFsm_. Because MyFsm_ cannot access to GuardSS in the original code.
In addition, you need to define a member variable bool m_guardVal=false; in MyFsm_. Because it is referred by GuardSS.
Here is updated code:
struct MyFsm_ : msmf::state_machine_def<MyFsm_>
{
// Guards (moved to parent from sub)
struct GuardSS {
template <class Event, class Fsm, class SourceState, class TargetState>
bool operator()(Event const&, Fsm& fsm, SourceState&, TargetState&)
{
if (fsm.m_guardVal == true){
std::cout << "Transition Approved by Guard \n";
return true;
}
std::cout << "Transition Rejected by Guard \n";
return false;
}
};
bool m_guardVal=false; // added
struct State1 : msmf::state<>{
// ... snip ...
Running demo is https://wandbox.org/permlink/V4UJJo3csX427rYi
I want to use a custom allocator to allocate memory from a freelist for std::basic_ostringstream. Here is my custom allocator which I want to use:
template <class Tp>
struct NAlloc {
typedef Tp value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
NAlloc() = default;
template <class T> NAlloc(const NAlloc<T>&) {}
Tp* allocate(std::size_t n) {
n *= sizeof(Tp);
memoryPool *memPool = memoryPool::GetInstance(10);//get memory pool instance
std::cout << "allocating " << n << " bytes\n";
return static_cast<Tp*>(memPool->allocate(n)); //get memory from pool
}
void deallocate(Tp* p, std::size_t n) {
std::cout << "deallocating " << n*sizeof*p << " bytes\n";
memoryPool *memPool = memoryPool::GetInstance(10);
memPool->deallocate(static_cast<void*>(p));//return memory to pool
}
template<typename U>
struct rebind {
typedef NAlloc<U> other;
};
Then, I use it like this:
typedef std::basic_string<char, std::char_traits<char>, NAlloc<char>> OstringStream;
****Problem:****
int main()
{
OstringStream os; //Object creation
os << " Hello, this is OstreamStream class with memory pool"; //here I am getting error
}
Error: 'OstringStream {aka std::basic_string<char, std::char_traits<char>, NAlloc<char> >}' is not derived from 'std::basic_ostream<_CharT, _Traits>'
Your OstringStream type is a typedef of std::basic_string, not of std::basic_ostream. That is why you are getting the error from operator<<. The left-hand operand must be an object derived from std::basic_ostream, exactly as the error message is saying.
std::basic_ostream itself does not use an allocator at all. It uses std::basic_streambuf for all of its I/O. For instance, std::ostringstream uses std::stringbuf, which uses the default std::allocator.
In C++11, std::basic_ostringstream has an optional Allocator template parameter, which it passes down to its internal std::basic_stringbuf. So, you could write your typedef like this instead:
typedef std::basic_ostringstream<char, std::char_traits<char>, NAlloc<char>> OstringStream;
int main()
{
OstringStream os;
os << " Hello, this is OstringStream with memory pool";
}
In earlier C++ versions, you would have to:
define a typedef of std::basic_stringbuf that uses your custom allocator instead of the default allocator.
construct a standard std::ostream object that uses an instance of your custom stringbuf type.
For example:
typedef std::basic_stringbuf<char, std::char_traits<char>, NAlloc<char> > Stringbuf_NAlloc;
class OstringStream : public Stringbuf_NAlloc, public std::ostream
{
public:
OstringStream() : Stringbuf_NAlloc(std::ios_base::out), std::ostream(this) {}
};
int main()
{
OstringStream os;
os << " Hello, this is OstringStream with memory pool";
}
In either case, know that the os.str() method will no longer return a standard std::string, which uses the default allocator. It will return a std::basic_string that uses your custom allocator instead. That will cause problems when trying to assign the return value of os.str() to a standard std::string, eg:
std::string s = os.str(); // error!
error: conversion from ‘std::__cxx11::basic_ostringstream<char, std::char_traits<char>, NAlloc>::__string_type {aka std::__cxx11::basic_string<char, std::char_traits<char>, NAlloc>}’ to non-scalar type ‘std::__cxx11::string {aka std::__cxx11::basic_string<char>}’ requested
So be aware of that. The STL is not very flexible when it comes to mixing allocators, so if you use a custom allocator, you usually have to apply it to every type of data container you use from the STL.
In the following example, while the current execution is still in substate1, I want to pass data to substate1 continuously and then send Event3 or Event1 based on the data. Looks like MSM supports only sending events using (process_event()), but I am not sure how to send data continuously to the current state.
#include <iostream>
#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>
namespace {
namespace msm = boost::msm;
namespace msmf = boost::msm::front;
namespace mpl = boost::mpl;
// ----- Events
struct Event1 {};
struct Event2 {};
struct Event3 {};
// ----- State machine
struct OuterSm_:msmf::state_machine_def<OuterSm_>
{
struct State1_:msmf::state_machine_def<State1_>
{
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, OuterSm_>::value));
std::cout << "State1::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, OuterSm_>::value));
std::cout << "State1::on_exit()" << std::endl;
}
struct SubState1:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, State1_>::value));
std::cout << "SubState1::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, State1_>::value));
std::cout << "SubState1::on_exit()" << std::endl;
}
};
struct SubState2:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, State1_>::value));
std::cout << "SubState2::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, State1_>::value));
std::cout << "SubState2::on_exit()" << std::endl;
}
};
// Set initial state
typedef mpl::vector<SubState1> initial_state;
// Transition table
struct transition_table:mpl::vector<
// Start Event Next Action Guard
msmf::Row < SubState1, Event2, SubState2, msmf::none, msmf::none >,
msmf::Row < SubState2, Event3, SubState1, msmf::none, msmf::none >
> {};
};
struct State2:msmf::state<>
{
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, OuterSm_>::value));
std::cout << "State2::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, OuterSm_>::value));
std::cout << "State2::on_exit()" << std::endl;
}
};
typedef msm::back::state_machine<State1_> State1;
// Set initial state
typedef State1 initial_state;
// Transition table
struct transition_table:mpl::vector<
// Start Event Next Action Guard
msmf::Row < State1, Event1, State2, msmf::none, msmf::none >
> {};
};
// Pick a back-end
typedef msm::back::state_machine<OuterSm_> Osm;
void test()
{
Osm osm;
osm.start();
std::cout << "> Send Event2()" << std::endl;
osm.process_event(Event2());
std::cout << "> Send Event1()" << std::endl;
osm.process_event(Event1());
}
}
int main()
{
test();
return 0;
}
Output:
State1::on_entry()
SubState1::on_entry()
> Send Event2()
SubState1::on_exit()
SubState2::on_entry()
> Send Event1()
SubState2::on_exit()
State1::on_exit()
State2::on_entry()
You can use get_state to retrieve a state from the state machine and then you can call an arbitrary method on that state, e.g. sendData() in the following modification of your example (live example):
#include <iostream>
#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>
namespace {
namespace msm = boost::msm;
namespace msmf = boost::msm::front;
namespace mpl = boost::mpl;
// ----- Events
struct Event1 {};
struct Event2 {};
struct Event3 {};
// ----- State machine
struct OuterSm_:msmf::state_machine_def<OuterSm_>
{
struct State1_:msmf::state_machine_def<State1_>
{
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, OuterSm_>::value));
std::cout << "State1::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, OuterSm_>::value));
std::cout << "State1::on_exit()" << std::endl;
}
struct SubState1:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, State1_>::value));
std::cout << "SubState1::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, State1_>::value));
std::cout << "SubState1::on_exit()" << std::endl;
}
void sendData(const std::string& data)
{
std::cout << "data received in Substate1:" << data << std::endl;
}
};
struct SubState2:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, State1_>::value));
std::cout << "SubState2::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, State1_>::value));
std::cout << "SubState2::on_exit()" << std::endl;
}
};
// Set initial state
typedef mpl::vector<SubState1> initial_state;
// Transition table
struct transition_table:mpl::vector<
// Start Event Next Action Guard
msmf::Row < SubState1, Event2, SubState2, msmf::none, msmf::none >,
msmf::Row < SubState2, Event3, SubState1, msmf::none, msmf::none >
> {};
};
struct State2:msmf::state<>
{
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, OuterSm_>::value));
std::cout << "State2::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, OuterSm_>::value));
std::cout << "State2::on_exit()" << std::endl;
}
};
typedef msm::back::state_machine<State1_> State1;
// Set initial state
typedef State1 initial_state;
// Transition table
struct transition_table:mpl::vector<
// Start Event Next Action Guard
msmf::Row < State1, Event1, State2, msmf::none, msmf::none >
> {};
};
// Pick a back-end
typedef msm::back::state_machine<OuterSm_> Osm;
void test()
{
Osm osm;
osm.start();
Osm::State1& state1 = osm.get_state<Osm::State1&>();
Osm::State1::SubState1& substate1 = state1.get_state<Osm::State1::SubState1&>();
substate1.sendData("hello from outside");
std::cout << "> Send Event2()" << std::endl;
osm.process_event(Event2());
std::cout << "> Send Event1()" << std::endl;
osm.process_event(Event1());
}
}
int main()
{
test();
return 0;
}
Output:
State1::on_entry()
SubState1::on_entry()
data received in Substate1:hello from outside
> Send Event2()
SubState1::on_exit()
SubState2::on_entry()
> Send Event1()
SubState2::on_exit()
State1::on_exit()
State2::on_entry()
I have dug around quite a bit today and have come up empty. Is there any way to store a functor that is returned from a boost::bind with different types? I found an example that used boost::variants but not sure that this is needed. (Foo and Bar have been simplified for simplicity sake)
#include <boost/bind.hpp>
#include <boost/variant.hpp>
#include <boost/function.hpp>
#include <map>
#include <iostream>
template <typename FooType>
struct Foo {
const FooType tmp_value;
Foo(const FooType& tmp_) :
tmp_value(tmp_)
{
}
template<typename Object>
void operator()(Object& operand)
{
std::cout << operand << std::endl;
operand += tmp_value;
}
};
template <typename BarType>
struct Bar {
const BarType tmp_value;
Bar(const BarType& tmp_) :
tmp_value(tmp_)
{
}
template<typename Object>
void operator()(Object& operand)
{
std::cout << operand << std::endl;
operand -= tmp_value;
}
};
typedef boost::variant<
boost::function<void(int32_t)>,
boost::function<void(int64_t)>,
boost::function<void(double)>,
boost::function<void(float)>
> my_functions;
typedef std::map<std::string, my_functions> test_map;
enum test_t {
FOO,
BAR
};
test_map createFunMap() {
test_map result;
for(int i = 0; i < 2; i++) {
switch(i) {
case test_t::FOO: {
std::cout << "In FOO" << std::endl;
Foo<double> p(1.0);
result.insert(std::pair<std::string,
boost::function<void(double)>>
("foo", boost::bind<void>(p, _1)));
break;
}
case test_t::BAR: {
std::cout << "In BAR" << std::endl;
Bar<int32_t> p(1.0);
result.insert(std::pair<std::string,
boost::function<void(int32_t)>>
("bar", boost::bind<void>(p, _1)));
break;
}
default:
std::cout << "just a default" << std::endl;
break;
}
}
return result;
}
int main() {
test_map myMap;
double t = 5.0;
myMap = createFunMap();
std::cout << t << std::endl;
myMap["foo"](t);
std::cout << t << std::endl;
return 0;
}
compiler output:
g++ -Wall --std=c++0x -I. test_ptrs.cc -o test_ptrs
test_ptrs.cc:93:2: error: type 'mapped_type' (aka 'boost::variant<boost::function<void (int)>, boost::function<void (long long)>, boost::function<void (double)>, boost::function<void (float)>,
boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_,
boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_,
boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_>') does not provide a call operator
myMap["foo"](t);
^~~~~~~~~~~~
1 error generated.
Thanks.
You have polymorphic functors (Foo and Bar).
You want to type erase them for a certain set of operand types. I suggest defining a type-erased functor type for the purpose:
struct ErasedFunctor
{
template<typename F> ErasedFunctor(F&& f)
: pimpl(new impl_<F>(std::forward<F>(f))) {}
template <typename T>
void operator()(T& oper) const {
assert(pimpl);
pimpl->call(oper);
}
private:
typedef boost::variant<int32_t&, int64_t&, double&, float&> Operand;
struct base_ { virtual void call(Operand oper) const = 0; };
// struct impl_ : base_ ...
std::shared_ptr<base_> pimpl;
};
Now you can simply store the function objects directly in the map:
typedef std::map<std::string, ErasedFunctor> test_map;
test_map createFunMap() {
return test_map {
{ "foo", Foo<double>(1.0) },
{ "bar", Bar<int32_t>(1) },
};
}
Let's use at("foo") instead of ["foo"] to avoid having to make ErasedFunctor default-constructible:
int main() {
test_map myMap = createFunMap();
double t = 5.0;
std::cout << t << std::endl;
myMap.at("foo")(t);
std::cout << t << std::endl;
myMap.at("bar")(t);
std::cout << t << std::endl;
}
Prints
5
void ErasedFunctor::apply::operator()(const F&, T&) const [with F = Foo<double>; T = double](5)
5
6
void ErasedFunctor::apply::operator()(const F&, T&) const [with F = Bar<int>; T = double](6)
6
5
See it Live On Coliru
For more background see:
Generating an interface without virtual functions?
Full Sample
#include <boost/bind.hpp>
#include <boost/variant.hpp>
#include <iostream>
template <typename FooType> struct Foo {
const FooType tmp_value;
Foo(const FooType &tmp_) : tmp_value(tmp_) {}
template <typename Object> void operator()(Object &operand) const {
std::cout << operand << std::endl;
operand += tmp_value;
}
};
template <typename BarType> struct Bar {
const BarType tmp_value;
Bar(const BarType &tmp_) : tmp_value(tmp_) {}
template <typename Object> void operator()(Object &operand) const {
std::cout << operand << std::endl;
operand -= tmp_value;
}
};
struct ErasedFunctor
{
template<typename F> ErasedFunctor(F&& f)
: pimpl(new impl_<F>(std::forward<F>(f))) {}
template <typename T>
void operator()(T& oper) const {
assert(pimpl);
pimpl->call(oper);
}
private:
typedef boost::variant<int32_t&, int64_t&, double&, float&> Operand;
struct base_ { virtual void call(Operand oper) const = 0; };
struct apply : boost::static_visitor<void> {
template <typename F, typename T> void operator()(F const& f, T& v) const {
std::cout << __PRETTY_FUNCTION__ << "(" << v << ")\n";
f(v);
}
};
template <typename F> struct impl_ : base_ {
F f_;
impl_(F&& f) : f_(std::forward<F>(f)) { }
virtual void call(Operand oper) const override {
boost::apply_visitor(boost::bind(apply(), boost::cref(f_), _1), oper);
}
};
std::shared_ptr<base_> pimpl;
};
#include <map>
typedef std::map<std::string, ErasedFunctor> test_map;
test_map createFunMap() {
return test_map {
{ "foo", Foo<double>(1.0) },
{ "bar", Bar<int32_t>(1) },
};
}
int main() {
test_map myMap = createFunMap();
double t = 5.0;
std::cout << t << std::endl;
myMap.at("foo")(t);
std::cout << t << std::endl;
myMap.at("bar")(t);
std::cout << t << std::endl;
}