This is a follow up problem that I opened up earlier in another thread at Boost: De-serializing a custom C++ object passed over ZeroMQ pull socket. The problem in that thread has been resolved based on the answer provided. Now I have another problem at runtime. Please, see the below description. I am realtively new to C++ realm so I appreciate if you tell me any necessity for improvement in any part of the code provided in addition to what I descibed under problem statment.
Description:
I have a C++ class named GenericMessage which simply holds an id and data as its members (See code snippet 2 below - GenericMessage.hxx). My intention is to serialize an instance of this class and send it via a ZeroMQ socket which implements the push pattern.
The serialization and sending task has been implemented in class ZMQHandler (see sendToBE function) which is placed in a header file name ZMQHandler.hxx shown in the code snippet 3 below. This class is instantiated by TestFE.cxx shown in the 4rd code snippet below.
The receiving and de-serialization of the GenericMessage instance is implemented in TestBE.cxx available in the 5th code snippet below. My intention is to receive the GenericMessage instance over the ZMQ socket (i.e. pull socket), de-serialize it and then print its members to standard output.
I verified that seriazalition and the GenericMessage object that is transferred over the ZeroMQ socket works fine. Deserialization seems to work as well cause I dont get any exception or segmentation fault like thing.
Problem Statement:
What is expected from the code in TestBE.cxx (see code snippet 5) is to receive the GenericMessage object over the ZeroMQ socket deserialize it and then print its two members namely id and data which is a string object in this case. More precisely it should first print the content of the char stream it gets and then the members of the de-serialized object. Instead, it does not print these members at all. Al, it puts weird symbols including question marks into the received char stream. Please see the 1st code snippet below, you'll see my point.
QUESTIONs:
i) Why cannot I get the expected output? Why do I see the question marked weird symbols in the output? Why I don't see the id and data fields printed although they are visible in the printed char stream?
ii) The data field in the GenericMessage class is a template type which is set to std::string for testing purposes. However, in real use, I plan to transfer a much more complex object in a serialized form. In this respect, do you think the use of classes boost::archive::text_iarchive and boost::archive::text_oarchive is useful. Should I use binary instead? If so, is there some pitfalls/possible problems that you think I should be aware of? Thanks in advance.
SNIPPET 1: Program output vs. expected output
*******************
The EXPECTED OUTPUT
*******************
Connecting to FE...
CHAR [22 serialization::archive 9 0 1 0
0 1 12 Hello there!]
ID: 1
Data: Hello there!
CHAR [22 serialization::archive 9 0 1 0
0 2 12 Hello there!]
ID: 2
Data: Hello there!
CHAR [22 serialization::archive 9 0 1 0
0 3 12 Hello there!]
ID: 3
Data: Hello there!
......
*************************
PRINTED OUTPUT IN REALITY
*************************
Connecting to FE...
CHAR [22 serialization::archive 9 0 1 0
0 1 12 Hello there!]
ID: 1
Data: Hello there!
//continues in increasing order same as above until the 18th message in the following
CHAR [22 serialization::archive 9 0 1 0
0 18 12 Hello there!]
ID: 0
Data:
//!!!!AFTER the 18th message I got question marks in the printed char stream!!!!!
CHAR [22 serialization::archive 9 0 1 0
0 19 12 Hello there!���]
ID: 0
Data:
CHAR [22 serialization::archive 9 0 1 0
0 20 12 Hello there!���]
ID: 0
Data:
CODE SNIPPET 2 (GenericMessage.hxx)
#include <iostream>
#include <string>
#include <sstream>
#include <boost/serialization/serialization.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
template <class T>
class GenericMessage {
public:
GenericMessage():
beId(-1)
{}
GenericMessage(int id, T msg):
beId(id),
data(msg)
{}
~GenericMessage(){}
T getData()
{
return data;
}
std::string toString()
{
std::ostringstream ss;
ss << getBeId();
std::string ret = ss.str();
return ("ID: " + ret + " DATA: " + getData());
}
void setBeId(int id)
{
beId = id;
}
int getBeId()
{
return beId;
}
private:
friend class boost::serialization::access;
int beId;
T data;
template <class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & beId;
ar & data;
}
};
CODE SNIPPET 3 (ZmqHandler.hxx)
#include "zmq.hpp"
#include "GenericMessage.hxx"
#include <unistd.h>
#include <cassert>
template <class A>
class ZmqHandler {
public:
ZmqHandler():
mContext(1),
mOutbHandlerSocket(mContext, ZMQ_PUSH)
{
mOutbHandlerSocket.bind ("tcp://*:5555");
}
~ZmqHandler() {}
void sendToBE(GenericMessage<A> *theMsg)
{
std::ostringstream archive_stream;
boost::archive::text_oarchive archive(archive_stream);
try
{
archive << theMsg;
} catch (boost::archive::archive_exception& ex) {
std::cout << "Archive Exception during deserializing:" << std::endl;
std::cout << ex.what() << std::endl;
} catch (int e) {
std::cout << "EXCEPTION " << e << std::endl;
}
std::string outbound_data_ = archive_stream.str();
// no need to use the c-style string function 'strlen'
int len = outbound_data_.length();
zmq::message_t msgToSend(len);
memcpy( msgToSend.data(), outbound_data_.data(), len );
mOutbHandlerSocket.send(msgToSend);
std::cout << "SENT request: [" << theMsg->toString() << "]" << std::endl;
std::cout << "LENGTH [" << len << "]" << std::endl;
}
private:
zmq::context_t mContext;
zmq::socket_t mOutbHandlerSocket;
};
CODE SNIPPET 4 (TestFE.cxx)
#include "ZmqHandler.hxx"
int main ()
{
ZmqHandler<std::string> zmqHandler;
int counter = 1;
while(1)
{
std::string data = "Hello there!";
GenericMessage<std::string> msg(counter, data);
zmqHandler.sendToBE(&msg);
counter++;
sleep(1);
}
return 0;
}
CODE SNIPPET 5 (TestBE.cxx)
#include "zmq.hpp"
#include "GenericMessage.hxx"
#include <fstream>
int main ()
{
// Prepare our context and socket
zmq::context_t context (1);
zmq::socket_t socket (context, ZMQ_PULL);
std::cout << "Connecting to FE..." << std::endl;
socket.connect ("tcp://localhost:5555");
while(1){
zmq::message_t reply;
socket.recv (&reply);
const char *buf = static_cast<const char*>(reply.data());
std::cout << "CHAR [" << buf << "]" << std::endl;
std::string input_data_( buf, reply.size() );
std::istringstream archive_stream(input_data_);
boost::archive::text_iarchive archive(archive_stream);
GenericMessage<std::string> theMsg;
try
{
archive >> theMsg;
} catch (boost::archive::archive_exception& ex) {
std::cout << "Archive Exception during deserializing:" << std::endl;
std::cout << ex.what() << std::endl;
} catch (int e) {
std::cout << "EXCEPTION " << e << std::endl;
}
std::cout << "ID: " << theMsg.getBeId() << std::endl;
std::cout << "Data: " << theMsg.getData() << std::endl;
}
return 0;
}
When I build and run your code on my system, TestBE does throw the deserialization exception (every time). Here's what I did to fix it:
In your ZmqHandler class, change the method void sendToBE(GenericMessage<A> *theMsg) to void sendToBE(GenericMessage<A> theMsg). You can use a const& if you want, but you probably don't want to use a pointer here. In the same method, you need to change theMsg->XXX to theMsg.XXX, since theMsg is no longer a pointer.
In TestFE, zmqHandler.sendToBE(&msg); becomes zmqHandler.sendToBE(msg);.
If theMsg must be a pointer
In ZmqHandler, just change the line archive << theMsg to archive << *theMsg. That way, the archive's operator<< is working with the object, rather than a pointer to the object. The rest of your code can remain the same.
Related
Use the answer in the question: simultaneous read and write to child's stdio using boost.process,
I refactored the code and hybridized the new method using the Boost library. I've been successful in making a pipes connection with Stockfish, but this is also where I get errors I've never seen before, not even Google helps.
Here is what I have tried:
#include <stdio.h>
#include <time.h>
#include <string>
#include <memory.h>
#include <unistd.h>
#include <iostream>
#include <stddef.h>
#include <execinfo.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fstream>
#include </usr/local/include/backtrace.h>
#include </usr/local/include/backtrace-supported.h>
#include <boost/process.hpp>
#include <boost/asio.hpp>
#include <boost/process/async.hpp>
#include <vector>
#include <iomanip>
#include <stdlib.h>
#include <string.h>
using namespace std;
namespace bp = boost::process;
using boost::system::error_code;
using namespace std::chrono_literals;
string errDetails = "Error Details: ";
void delay(int number_of_seconds) {
int ms = 1000 * number_of_seconds;
clock_t start_time = clock();
while (clock() < start_time + ms)
;
}
static void full_write(int fd, const char* buf, size_t len) {
while (len > 0) {
ssize_t ret = write(fd, buf, len);
if ((ret == -1) && (errno != EINTR)) {
break;
}
buf += (size_t) ret;
len -= (size_t) ret;
}
}
void print_backtrace() {
static const char start[] = "--------BACKTRACE--------\n\n";
static const char end[] = "-------------------------\n\n";
void *bt[1024];
int bt_size;
char **bt_syms;
int i;
bt_size = backtrace(bt, 1024);
bt_syms = backtrace_symbols(bt, bt_size);
full_write(STDERR_FILENO, start, strlen(start));
full_write(STDERR_FILENO, errDetails.c_str(), strlen(errDetails.c_str()));
for (i = 1; i < bt_size; i++) {
size_t len = strlen(bt_syms[i]);
full_write(STDERR_FILENO, bt_syms[i], len);
full_write(STDERR_FILENO, "\n", 1);
}
full_write(STDERR_FILENO, end, strlen(end));
free(bt_syms);
}
void abort_application() {
size_t memLeakCount, staticMemLeakCount;
uint64_t memLeakSize, staticMemLeakSize;
for (int i = 0; i < 3; i++) {
/**
* Delay
*/
delay(1);
}
print_backtrace();
abort();
}
inline bool stockfish_check_exists(const std::string& name) {
struct stat buffer;
return (stat(name.c_str(), &buffer) == 0);
}
int main() {
std::future<std::string> data;
boost::asio::io_service svc;
bp::async_pipe in{svc}, out{svc};
string proc = "";
char command[64];
string output = "";
if (stockfish_check_exists("stockfish")) {
proc = "stockfish"; } else {
errDetails = "Stockfish not found!\n\n";
abort_application();
}
std::string const program_dir = proc;
auto on_exit = [](int code, std::error_code ec) {
std::cout << "Exited " << code << "(" << ec.message() << ")\n";
};
bp::child process(proc, bp::std_in < in, svc);
boost::asio::streambuf recv_buffer;
std::cout << "uci send" << std::endl;
boost::asio::async_write(in, boost::asio::buffer("uci\n"),
[&](boost::system::error_code ec, size_t transferred) {
std::cout << "Write: " << transferred << "\n" << std::endl;
in.close();
}
);
std::cout << "isready send" << std::endl;
boost::asio::async_write(in, boost::asio::buffer("isready\n"),
[&](boost::system::error_code ec, size_t transferred) {
std::cout << "Write: " << transferred << "\n" << std::endl;
in.close();
}
);
cout << "Enter your command: ";
cin >> command;
cout << "Your command is: " << command << endl;
if (strcmp(command, "quit") == 0) {
cout << "Quiting......." << endl;
boost::asio::async_write(in, boost::asio::buffer("quit"),
[&](boost::system::error_code ec, size_t transferred) {
std::cout << "Write: " << transferred << std::endl;
in.close();
cout << "Engine quit!" << endl;
}
);
}
svc.run();
return 0;
}
To make it easier to follow, I left out std::std_out > out at the line:
bp::child process(proc, bp::std_in < in, svc);
so that the engine results are immediately displayed in the Terminal window, so I'll know if I've gone astray. And this is when I discovered the strange thing
When I launch the application, it outputs on Terminal as follows:
[2022-01-14 20:25:55]
duythanh#DuyThanhs-MacBook-Pro:/Volumes/Data/ChessGUI$ ./ChessGUI
uci send
isready send
Enter your command: Stockfish 120122 by the Stockfish developers (see AUTHORS file)
id name Stockfish 120122
id author the Stockfish developers (see AUTHORS file)
option name Debug Log File type string default
option name Threads type spin default 1 min 1 max 512
option name Hash type spin default 16 min 1 max 33554432
option name Clear Hash type button
option name Ponder type check default false
option name MultiPV type spin default 1 min 1 max 500
option name Skill Level type spin default 20 min 0 max 20
option name Move Overhead type spin default 10 min 0 max 5000
option name Slow Mover type spin default 100 min 10 max 1000
option name nodestime type spin default 0 min 0 max 10000
option name UCI_Chess960 type check default false
option name UCI_AnalyseMode type check default false
option name UCI_LimitStrength type check default false
option name UCI_Elo type spin default 1350 min 1350 max 2850
option name UCI_ShowWDL type check default false
option name SyzygyPath type string default <empty>
option name SyzygyProbeDepth type spin default 1 min 1 max 100
option name Syzygy50MoveRule type check default true
option name SyzygyProbeLimit type spin default 7 min 0 max 7
option name Use NNUE type check default true
option name EvalFile type string default nn-ac07bd334b62.nnue
uciok
Unknown command: isready
Contrasting with the code above, the two commands were sent through pipes. is uci and isready, this is fine. The first uci command runs successfully, but the isready command, instead of returning readyok, it returns:
Unknown command: isready
I keep trying to type quit, which sends a quit command to the pipe as the exit engine, and it also fails:
Your command is: quit
Quiting.......
Write: 5
Write: 9
Unknown command: quit
Write: 5
Engine quit!
The program will then exit with the engine. I'm still wondering what was going on at the time, but the clues are really hazy as to what was going on behind the scenes.
Please help me. Any help is highly appreciated. Thank you so much everyone
UPDATE: The error continued when Unknown Command: Quit appeared. I typed these commands in Terminal while running Stockfish directly through Terminal, they work as a result, but my program still can't
You are printing to cout as if the async operations happen immediately. That's not the case. The async operations only happen when the io service runs.
svc.run();
Is at the very end of your code. So no async_ operation ever completes (or even starts) before that.
Other problems:
Your out async pipe is never used (not even connected). It's unclear to me how you intend to communicate with the child process that way.
In fairness, you only every write to the child process, so maybe you're not at all interested in the output. (But then perhaps recv_buffer can be deleted just as well).
Your buffers include the terminating NUL characters. (asio::buffer("uci\n") sends {'u','c','i','\n','\0'}). That's going to mess up the child processes's parsing.
You do in.close() in response to every single async_write completion. This guarantees that subsequent writes never can happen, as you closed the pipe.
Then when you send quit you fail to include the '\n' as well
You are reading into a char[64] with operator>> which makes no sense at all. Maybe you are using c++20 (so width of 64 might be assumed) but you never set a width. Most likely you would want to read into a string instead.
However, doing so cannot accept commands with whitespace (because std::ios::skipws is set by default). So, likely you wanted std::getline instead...
The fact that you include a boatload of C headers makes me think you're porting some C code (badly). That's also exemplified by the strcmp use and others, e.g. no need to use ::stat
Don't use using namespace std; (Why is "using namespace std;" considered bad practice?)
Don't use global variables (errDetails)
Don't use loops to wait for a time delay
No need to manually print backtraces. Instead, use Boost:
void abort_application(std::string const& errDetails) {
std::cerr << errDetails << "\n";
std::cerr << boost::stacktrace::stacktrace{} << std::endl;
std::this_thread::sleep_for(3s);
abort();
}
Existing Stockfish Client: Playing Games
You're in luck: I have a written full demo using stockfish on this site: Interfacing with executable using boost in c++.
This example shows how to correctly await and parse expected replies from the child process(es).
You will note that I chose coroutines for the async version:
Just for completeness, I thought I'd try an asynchronous implementation. Using the default Asio callback style this could become unwieldy, so I thought to use Boost Coroutine for the stackful coroutines. That makes it so the implementation can be 99% similar to the synchronous version
Just for comparison, here's what your code should look like if you didn't use coroutines:
Fixing Up Your Code
Live On Coliru
#include <boost/asio.hpp>
#include <boost/process.hpp>
#include <boost/process/async.hpp>
#include <boost/stacktrace/stacktrace.hpp>
#include <chrono>
#include <iomanip>
#include <iostream>
namespace bp = boost::process;
using boost::system::error_code;
using namespace std::literals;
static void abort_application(std::string const& errDetails) {
std::cerr << errDetails << "\n";
std::cerr << boost::stacktrace::stacktrace{} << std::endl;
std::this_thread::sleep_for(3s);
abort();
}
inline static bool stockfish_check_exists(std::string& name) {
return boost::filesystem::exists(name);
}
int main() {
boost::asio::io_service svc;
bp::async_pipe in{svc};
std::string proc = "/usr/games/stockfish";
if (!stockfish_check_exists(proc)) {
abort_application("Stockfish not found!");
}
auto on_exit = [](int code, std::error_code ec) {
std::cout << "Exited " << code << "(" << ec.message() << ")\n";
};
bp::child process(proc, bp::std_in < in, svc, bp::on_exit = on_exit);
std::function<void()> command_loop;
std::string command_buffer;
command_loop = [&] {
std::cout << "Enter your command: " << std::flush;
// boost::asio::streambuf recv_buffer;
if (getline(std::cin, command_buffer)) {
std::cout << "Your command is: " << command_buffer << std::endl;
command_buffer += '\n';
async_write( //
in, boost::asio::buffer(command_buffer),
[&](error_code ec, size_t transferred) {
std::cout << "Write: " << transferred << " (" << ec.message() << ")" << std::endl;
if (command_buffer == "quit\n") {
std::cout << "Quiting......." << std::endl;
// in.close();
std::cout << "Engine quit!" << std::endl;
} else {
command_loop(); // loop
}
});
}
};
std::cout << "uci send" << std::endl;
async_write(
in, boost::asio::buffer("uci\n"sv),
[&](error_code ec, size_t transferred) {
std::cout << "Write: " << transferred << "\n" << std::endl;
std::cout << "isready send" << std::endl;
async_write(in, boost::asio::buffer("isready\n"sv),
[&](error_code ec, size_t n) {
std::cout << "Write: " << n << std::endl;
command_loop(); // start command loop
});
});
svc.run(); // only here any of the operations start
}
Prints, e.g.
Or if Stockfish is in fact installed:
I've been battling this code all day now, and I'm close to pulling what remains of my hair.
I've got a Server and Client class, and the original goal was for the Server class to have a list of 'Clients' with which it could interact. The entire host of issues aside, I've got some basics working. The Server does register new connections, and I can even send strings from the Client.
Here's the deal though, when I try to send a string with spaces, the whole thing breaks down.
Here's my Send function
int Send(std::string message)
{
const char* cstr = message.c_str();
size_t len = message.length();
//The +1 is for the \0 character that c_str() adds
//this->server is a socket that has already been connected to and accepted by the server
int bytes = send(this->server, cstr, len + 1, 0);
return bytes;
}
On the server side of things:
void Run()
{
char buffer[1024];
while (1)
{
listen(server, 0);
SOCKET incoming_sock;
int clientAddrSize = sizeof(clientAddr);
if ((incoming_sock = accept(server, (SOCKADDR*)&clientAddr, &clientAddrSize)) != INVALID_SOCKET)
{
std::cout << "Error: " << WSAGetLastError() << std::endl;
std::cout << "Connection occured " << printIP(clientAddr.sin_addr.s_addr) << std::endl;
int bytes = recv(incoming_sock, buffer, sizeof(buffer), 0);
std::cout << bytes << " Bytes With the message: " << buffer << std::endl;
std::cout << "Error: " << WSAGetLastError() << std::endl;
}
else
{
std::cout << "Error: " << WSAGetLastError() << std::endl;
}
}
}
Here's the weird part:
In my Client's main function, when I predefine a string, like "Hello World" the Server prints it out just fine. But when I try to parse user input with std::cin, the message breaks down after the first blank space.
Client Main Function:
#include "Client.h"
#include <iostream>
int main()
{
Client c("127.0.0.1", 5555, 1);
std::string msg = "Hello World!";
while (msg.compare("exit") != 0)
{
//std::cout << "Send: ";
//std::cin >> msg;
int bytes = c.Send(msg);
std::cout << "Sent \"" << msg << "\"" << "Bytes: " << bytes << std::endl;
}
while (1);
return 0;
}
And the Output on the Server:
In the Constructor Error code: 0Bind code: 0
Error: 0
Connection occured 127.0.0.0
13 Bytes With the message: Hello World!
Error: 0
If I uncomment the input, and type in "Hello" in the prompt, I get the following output:
In the Constructor Error code: 0Bind code: 0
Error: 0
Connection occured 127.0.0.0
6 Bytes With the message: hello
Error: 0
But if I type "Hello World!"
I only get:
In the Constructor Error code: 0Bind code: 0
Error: 0
Connection occured 127.0.0.0
6 Bytes With the message: Hello
Error: 0
std::cin >> msg; reads up to the first whitespace. If you want to read a full line up to the line-end character, make it
std::getline(cin, msg);
Im am receiving boost::mpi messages using the irecv() function. I have a waiting loop, that calls test() on the request object returned by irecv and, if the request finished, does something. However, trying to figure out the sender's rank, I get an exception:
boost::optional<T>::reference_type boost::optional<T>::get() [with T = boost::mpi::status; boost::optional<T>::reference_type = boost::mpi::status&]: Assertion `this->is_initialized()' failed.
Here is my code snippet:
mpi::request inc = world.irecv(mpi::any_source, MpiHandler::MPI_RESULT, pxl_results);
do {
if(inc.test()) {
// fails here, as the optional<status> returned by inc.test() is not initialized.
world.send(inc.test().get().source(), MpiHandler::MPI_WORK, package);
...
}
} while(...);
If I check for inc.test().is_initialized(), I find that the optional<status> is indeed uninitialized. What is happening here, and why can I not find out anything about my MPI sender? Is it possibly the mpi::any_source that doesn't play well with irecv?
Just to add: Typically, the sender and tag of an MPI message can be found out from the request object like outlined in this answer.
I'm glad you worked it out, but maybe this will explain it further.
The issue is calling req.test() again after a successful call to req.test(). In MPI - The Complete Reference: Volume 1, The MPI Core:
A request object is deallocated automatically by a successful call to MPI_WAIT or MPI_TEST.
Also, from the boost mpi documentation:
optional< status > test();
Determine whether the communication associated with this request has completed successfully. If so, returns the status object describing the communication. Otherwise, returns an empty optional<> to indicate that the communication has not completed yet. Note that once test() returns a status object, the request has completed and wait() should not be called.
Therefore, after if(req.test()) returns successfully a boost::optional<mpi::status> a subsequent call to req.test() will likely return an empty optional<> leading to your exception.
To see this we first create an example from Jonathan Dursi's hello world example in the linked answer:
#include <boost/mpi.hpp>
#include <iostream>
#include <string>
#include <boost/serialization/string.hpp>
namespace mpi = boost::mpi;
int main()
{
mpi::environment env;
mpi::communicator world;
if (world.rank() == 0) {
std::string msg, out_msg = "Hello from rank 0.";
world.send(1, 17, out_msg);
} else {
mpi::request req;
std::string rmsg;
req = world.irecv(mpi::any_source, mpi::any_tag, rmsg);
do {
if(req.test()) {
// fails here, as the optional<status> returned by inc.test() is not initialized.
std::cout << "From " << req.test().get().source() << std::endl;
std::cout << "Got " << rmsg << std::endl;
break;
}
} while(1);
}
return 0;
}
Building and running this leads to the exception as expected:
[ronin:~/Documents/CPP] aichao% mpirun --hostfile hostfile -np 2 ./test_mpi_request
From Assertion failed: (this->is_initialized()), function get, file /Users/Shared/Tools/boost_1_53_0/boost/optional/optional.hpp, line 631.
To fix this:
Call req.test() to return a boost::optional<mpi::status> object.
Test that boost::optional object to see if req.test() returned successfully and if successful, use the returned mpi::status.
The code:
#include <boost/mpi.hpp>
#include <iostream>
#include <string>
#include <boost/serialization/string.hpp>
namespace mpi = boost::mpi;
int main()
{
mpi::environment env;
mpi::communicator world;
if (world.rank() == 0) {
std::string msg, out_msg = "Hello from rank 0.";
world.send(1, 17, out_msg);
} else {
mpi::request req;
std::string rmsg;
req = world.irecv(mpi::any_source, mpi::any_tag, rmsg);
do {
boost::optional<mpi::status> stat = req.test();
if (stat) {
std::cout << "From " << stat->source() << std::endl;
std::cout << "Got " << rmsg << std::endl;
std::cout << "Tagged " << stat->tag() << std::endl;
break;
}
} while(1);
}
return 0;
}
Now, we have success:
[ronin:~/Documents/CPP] aichao% mpirun --hostfile hostfile -np 2 ./test_mpi_request
From 0
Got Hello from rank 0.
Tagged 17
I figured it out: The problem was that I called test() again, which apparently invalidates something. It works with
auto ret = inc.test();
if(ret) {
// use ret->source()
}
I've made a simple module for transferring data. Most of my code is based on code I found on SO
When I run the instances on my local machine, everything works fine. But if I try running this over a local area network, I get the error "Can't Assign Requested Address".
Note: My "instances basically involve running ./server 1 0 and ./server 1 1 so, they;re waiting for data. And then ./server 0 to send it over.
Here's the code
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <iostream>
#include <boost/serialization/string.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/asio.hpp>
static std::string const server_ip = "10.0.0.4";
static std::string const client_ip = "10.0.0.5";
using std::cout;
using std::endl;
struct Location {
double rent[6];
double cost[7];
std::string name, group;
std::string locationOfObjectFile;
int locationNo;
template <typename Ar> void serialize(Ar &ar, unsigned) {
ar &rent;
ar &cost;
ar &name;
ar &group;
ar &locationOfObjectFile;
ar &locationNo;
}
};
struct Player {
int currentPosition;
double currentMoney;
template <typename Ar> void serialize(Ar &ar, unsigned) {
ar ¤tPosition;
ar ¤tMoney;
}
};
struct Monopoly {
std::vector<Location> locations;
std::vector<Player> players;
std::string currency;
template <typename Ar> void serialize(Ar &ar, unsigned) {
ar &locations;
ar &players;
ar ¤cy;
}
};
Location l1;
Player p1;
Monopoly game, game2;
void readData(int x) {
boost::asio::io_service io_service;
uint16_t port = x;
boost::asio::ip::tcp::acceptor acceptor(
io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(server_ip), port));
/* code */
boost::asio::ip::tcp::socket socket(io_service);
acceptor.accept(socket);
std::cout << "connection from " << socket.remote_endpoint() << std::endl;
// read header
size_t header;
boost::asio::read(socket, boost::asio::buffer(&header, sizeof(header)));
std::cout << "body is " << header << " bytes" << std::endl;
// read body
boost::asio::streambuf buf;
const size_t rc = boost::asio::read(socket, buf.prepare(header));
buf.commit(header);
std::cout << "read " << rc << " bytes" << std::endl;
// deserialize
std::istream is(&buf);
boost::archive::text_iarchive ar(is);
ar &game2;
cout << game2.locations[0].rent[1] << endl;
cout << game2.players[0].currentPosition << "how cool is this?";
socket.close();
}
void sendData() {
for (int i = 0; i <= 1; i++) {
boost::asio::streambuf buf;
std::ostream os(&buf);
boost::archive::text_oarchive ar(os);
ar &game;
boost::asio::io_service io_service;
boost::asio::ip::tcp::socket socket(io_service);
short port = i + 1234;
socket.connect(boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(client_ip), port));
const size_t header = buf.size();
std::cout << "buffer size " << header << " bytes" << std::endl;
// send header and buffer using scatter
std::vector<boost::asio::const_buffer> buffers;
buffers.push_back(boost::asio::buffer(&header, sizeof(header)));
buffers.push_back(buf.data());
const size_t rc = boost::asio::write(socket, buffers);
std::cout << "wrote " << rc << " bytes" << std::endl;
;
socket.close();
}
}
int main(int argc, char **argv) {
l1.name = "soemthig";
l1.group = 2;
p1.currentMoney = 300;
p1.currentPosition = 422;
for (int i = 0; i < 7; ++i) {
l1.cost[i] = i;
/* code */
}
for (int i = 0; i < 6; ++i) {
l1.rent[i] = 2 * i;
/* code */
}
l1.locationOfObjectFile = "ajhsdk/asdc.obj";
l1.locationNo = 5;
game.locations.push_back(l1);
game.players.push_back(p1);
game.currency = "dollar";
cout << game.currency;
if (atoi(argv[1]) ==
1) // argv[0]=0 implies server, argv[0]==1 implies client while argv[1] specifies 1st or second client
{
cout << "reading data";
if (atoi(argv[2]) == 0) {
readData(1234);
/* code */
} else {
readData(1235);
}
} else {
cout << "writing data";
sendData();
}
}
Here is the error message stack trace:
libc++abi.dylib: terminating with uncaught exception of type boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::system::system_error> >: bind: Can't assign requested address dollarreading dataAbort trap: 6 and 10.0.0.4 is the IP address of the computer that is supposed to send the data.
You get this when the connection is refused.
This, in turn, could happen when the client is not listening on the required interface.
Make sure that the IP addresses are actually the public IP addresses on the network and the machines can reach eachother. E.g. use netstat -tlpn (or similar on your OS) to ascertain that the clients are listening:
tcp 0 0 192.168.2.136:1234 0.0.0.0:* LISTEN 18186/valgrind.bin
tcp 0 0 192.168.2.136:1235 0.0.0.0:* LISTEN 18187/valgrind.bin
Now, try to connect using e.g. netcat from the server machine:
netcat 192.168.2.136 1234
This will probably crash the client, but it will also tell you whether connecting was possible.
If not, then either the address is not reachable, the client is not listening on the right interface, the firewall is filtering your traffic etc.
PS. I've made your code self contained and running live on Coliru. Please do this; making your code selfcontained make it possible for people to actually fix things
I have a C++ program that uses boost log, and I load a user-provided dynamic link library. I'd like to redirect stderr to the boost log, so that anytime the user's library does:
std::cerr << "Some stuff";
It produces the same result** as:
BOOST_LOG_SEV(log,info) << "Some stuff";
Is this possible, and if so then how do I do it?
(Also, I'm not sure what to do about the severity... since cerr << doesn't privide severity information. I'm open to suggestions on that as well...)
** By "same result" I mean that it gets logged to the same log file as the rest of the log messages, and the same log formatter is applied to those lines.
Here's my C++11 implementation. This class can be used for anything (not just boost) to capture stdout/stderr on a line-by-line basis and calling a user function (could be a lambda) to process it.
Warning: if you redirect stderr and stdout and are using Boost, then redirect stderr first, then stdout. Otherwise, Boost will write loopback the stderr message back to stdout, and you'll get a boost log entry inside another boost log entry.
Usage example
cout << "testing out before 1 2 3 " << endl;
cerr << "testing err before 1 2 3 " << endl;
{
StdErrHandler err([](const char* line){
BOOST_LOG_TRIVIAL(error) << "ERROR:" << strlen(line) << " " << line;
});
StdOutHandler out([](const char* line){
BOOST_LOG_TRIVIAL(info) << "OUT:" << strlen(line) << " " << line;
});
cout << "cout testing 1 2 3 " << endl;
cerr << "cerr testing 1 2 3 " << endl;
}
cout << "testing out after 1 2 3 " << endl;
cerr << "testing err after 1 2 3 " << endl;
Example output
pa-poca$ ./test
testing out before 1 2 3
testing err before 1 2 3
[2014-08-01 12:24:56.468335] [0x000007f89d8990d4] [error] ERROR:19 cerr testing 1 2 3
[2014-08-01 12:24:56.468360] [0x000007f89d8990d4] [info] OUT:19 cout testing 1 2 3
testing out after 1 2 3
testing err after 1 2 3
Code
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
class StdioHandler
{
private:
pid_t pid = 0;
int origfd;
int streamid;
int pipefd[2];
public:
enum class Stream
{
stdout = STDOUT_FILENO,
stderr = STDERR_FILENO
};
StdioHandler(Stream stream, std::function<void(const char*)> callback)
:streamid(static_cast<int>(stream))
{
origfd = dup(streamid);
pipe(pipefd); // create pipe
pid = fork(); //spawn a child process to handle output of pipe
if (pid == 0)
{
char line[256];
FILE* output;
close(pipefd[1]);
output = fdopen(pipefd[0], "r");
if (output)
{
while(fgets(line, sizeof(line), output))
{
int n = strlen(line);
if (n > 0)
if (line[n-1] == '\n') line[n-1] = 0;
callback(line);
}
fclose(output);
}
abort();
} else {
// connect input of pipe to
close(pipefd[0]);
dup2(pipefd[1], streamid);
}
}
~StdioHandler()
{
int status;
usleep(10000);
close(pipefd[1]);
kill(pid,SIGINT);
waitpid(pid, &status, 0);
dup2(origfd, streamid);
}
};
class StdOutHandler : public StdioHandler
{
public:
StdOutHandler(std::function<void(const char*)> callback) :
StdioHandler(Stream::stdout, callback)
{
}
};
class StdErrHandler : public StdioHandler
{
public:
StdErrHandler(std::function<void(const char*)> callback) :
StdioHandler(Stream::stderr, callback)
{
}
};
I am guessing , You can redirect File descriptor STDERR to your stream file descriptor [you have to get the file desc of your stream] using dup/dup2 api [its a posix api]