How to Print Boost::Any to a Stream

How to print a boost::variant of streamable types?

You should be able to stream a variant if all its contained types are streamable. Demonstration:

#include <boost/variant.hpp>
#include <iostream>
#include <iomanip>

struct MyType
{
boost::variant<int, char, bool> v;
};

std::ostream& operator<<(std::ostream &out, const MyType &type)
{
out << type.v;
}

int main()
{
MyType t;
t.v = 42;
std::cout << "int: " << t << std::endl;

t.v = 'X';
std::cout << "char: " << t << std::endl;

t.v = true;
std::cout << std::boolalpha << "bool: " << t << std::endl;
}

Output:

int: 42
char: X
bool: true

If you do need to use a visitor (perhaps because some of the contained types aren't streamable), then you need to apply it to the variant itself; your snippet of code looks like it's applying it to a MyType object instead.

c++ boost::any to define my own print ,

There is quite easy way to do this, described in "Beyond the C++ Standard Library: An Introduction to Boost":

struct streamer {
virtual void print(ostream &o, const boost::any &a) const =0;
virtual streamer * clone() const = 0;
virtual ~streamer() {}
};

template <class T>
struct streamer_impl: streamer{
void print(ostream &o, const boost::any &a) const { o << *boost::any_cast<T>(a); }
streamer *clone() const { return new streamer_impl<T>(); }
};

class any_out {
streamer *streamer_;
boost::any o_;
void swap(any_out & r){
std::swap(streamer_, r.streamer_);
std::swap(o_, r.o_);
}
public:
any_out(): streamer_(0) {}
template<class T> any_out(const T& value)
: streamer_(new streamer_impl<T>()), o_(value) {}
any_out(const any_out& a)
: streamer_(a.streamer_ ? a.streamer_->clone() : 0), o_(a.o_) {}

template <class T>
any_out & operator=(const T& r) {
any_out(r).swap(*this);
return *this;
}
~any_out() { delete streamer_; }

friend std::ostream &operator<<(std::ostream& o, const any_out & a) {
if(a.streamer_)
a.streamer_->print(o, a);
return o;
}
};

and then you use any_out instead of boost::any.

boost::format and custom printing a std containers

I think the most clean way is to provide a thin wrapper in your own namespace for each of the operators you want to override. For your case, it can be:

namespace ns
{
namespace wrappers
{
template<class T>
struct out
{
const std::set<T> &set;

out(const std::set<T> &set) : set(set) {}

friend std::ostream& operator<<(std::ostream& stream, const out &o)
{
stream << "{";
bool first = true;
for (const T& item : o.set)
{
if (!first)
stream << ", ";
else
first = false;
stream << item;
}
stream << "}";
return stream;
}
};
}

template<class T>
wrappers::out<T> out(const std::set<T> &set)
{
return wrappers::out<T>(set);
}
}

Then use it like this:

std::cout << boost::format("%1%") % ns::out(x);

Making boost::spirit::hold_any accept vectors

Firstly, that advice is 12 years old. Since then std::any was even standardized. I would not assume that hold_any is still the better choice (on the contrary).

Also, note that the answer you implied contains the exact explanation:

This class has two differences if compared to boost::any:

  • it utilizes the small object optimization idiom and a couple of other optimization tricks, making spirit::hold_any smaller and faster than boost::any
  • it has the streaming operators (operator<<() and operator>>()) defined, allowing to input and output a spirit::hold_any seemlessly.

(emphasis mine)

Incidentally, the whole question was about streaming any in the first place, so the answer was on-point there.

The code further drives home the assumption:

// these functions have been added in the assumption that the embedded
// type has a corresponding operator defined, which is completely safe
// because spirit::hold_any is used only in contexts where these operators
// do exist
template <typename Char_>
friend inline std::basic_istream<Char_>&
operator>> (std::basic_istream<Char_>& i, basic_hold_any<Char_>& obj)
{
return obj.table->stream_in(i, &obj.object);
}

template <typename Char_>
friend inline std::basic_ostream<Char_>&
operator<< (std::basic_ostream<Char_>& o, basic_hold_any<Char_> const& obj)
{
return obj.table->stream_out(o, &obj.object);
}

So, indeed that explains the requirement. It's a bit unfortunate that the implementation is not SFINAE-ed so that you'd only run into the limitation if you used the stream_in/stream_out operations, but here we are.

Boost.beast : how to return json response only

 binapi::AsyncRest::httpClient* client;

That's extremely suspect, since the class is using enable_shared_from_this(). Pretty sure that should be

auto client =
std::make_shared<binapi::AsyncRest::httpClient>(ioc.get_executor(), ctx);

Next up, I assume get_server_time is a static function. I don't see why it is a member of httpClient.

So adding all the missing code back in (using lots of experience):

#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/beast.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/url.hpp>
#include <boost/url/src.hpp> // for header-only
#include <fstream>
#include <iomanip>
#include <iostream>
namespace net = boost::asio;
namespace beast = boost::beast;
namespace http = boost::beast::http;
namespace ssl = boost::asio::ssl;
using net::ip::tcp;

namespace binapi { namespace AsyncRest {

// Report a failure
void fail_http(beast::error_code ec, char const* what) {
std::cerr << what << ": " << ec.message() << "\n";
}

struct httpClient : std::enable_shared_from_this<httpClient> {
using executor = net::any_io_executor;
using Stream = beast::ssl_stream<beast::tcp_stream>;

tcp::resolver resolver_;
Stream stream_;
beast::flat_buffer buffer_;
http::request<http::empty_body> req_;
http::response<http::string_body> res_;

httpClient(executor ex, ssl::context& ctx);

// Start the asynchronous operation
void run(boost::url, http::verb);
void on_resolve(beast::error_code, tcp::resolver::results_type);
void on_connect(beast::error_code, tcp::resolver::results_type::endpoint_type);
void on_handshake(beast::error_code);
void on_write(beast::error_code, size_t bytes_transferred);
void on_read(beast::error_code, size_t bytes_transferred);
void on_shutdown(beast::error_code);
};

httpClient::httpClient(executor ex, ssl::context& ctx)
: resolver_(ex)
, stream_(ex, ctx) {}

// Start the asynchronous operation
void httpClient::run(boost::url url, http::verb action) {

std::string const host(url.host());
std::string const service = url.has_port() //
? url.port()
: (url.scheme_id() == boost::urls::scheme::https) //
? "https"
: "http";
url.remove_origin(); // becomes req_.target()

// Set SNI Hostname (many hosts need this to handshake
// successfully)
if (!SSL_set_tlsext_host_name(stream_.native_handle(), host.c_str())) {
beast::error_code ec{static_cast<int>(::ERR_get_error()),
net::error::get_ssl_category()};
std::cerr << ec.message() << "\n";
return;
}

// Set up an HTTP GET/POST/DELETE/PUT request message
// req_.version(version);
req_.method(action);
req_.target(url.c_str());
req_.set(http::field::host, host);
req_.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
req_.prepare_payload(); // make HTTP 1.1 compliant

// Look up the domain name
resolver_.async_resolve(
host, service,
beast::bind_front_handler(&httpClient::on_resolve, shared_from_this()));
}

void httpClient::on_resolve(beast::error_code ec,
tcp::resolver::results_type results) {
if (ec)
return fail_http(ec, "resolve");

// Set a timeout on the operation
beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));

// Make the connection on the IP address we get from a lookup
beast::get_lowest_layer(stream_).async_connect(
results,
beast::bind_front_handler(&httpClient::on_connect, shared_from_this()));
}

void httpClient::on_connect(beast::error_code ec,
tcp::resolver::results_type::endpoint_type) {
if (ec)
return fail_http(ec, "connect");

// Perform the SSL handshake
stream_.async_handshake(
ssl::stream_base::client,
beast::bind_front_handler(&httpClient::on_handshake, shared_from_this()));
}

void httpClient::on_handshake(beast::error_code ec) {
if (ec)
return fail_http(ec, "handshake");

// Set a timeout on the operation
beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));

// Send the HTTP request to the remote host
std::cout << "Sending " << req_ << std::endl;
http::async_write(
stream_, req_,
beast::bind_front_handler(&httpClient::on_write, shared_from_this()));
}

void httpClient::on_write(beast::error_code ec, size_t bytes_transferred) {
boost::ignore_unused(bytes_transferred);

if (ec)
return fail_http(ec, "write");

// Receive the HTTP response
http::async_read(
stream_, buffer_, res_,
beast::bind_front_handler(&httpClient::on_read, shared_from_this()));
}

void httpClient::on_read(beast::error_code ec, size_t bytes_transferred) {
boost::ignore_unused(bytes_transferred);

if (ec)
return fail_http(ec, "read");

// Write the message to standard out
std::cout << res_ << std::endl;

// Set a timeout on the operation
beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));

// Gracefully close the stream
stream_.async_shutdown(
beast::bind_front_handler(&httpClient::on_shutdown, shared_from_this()));
}

void httpClient::on_shutdown(beast::error_code ec) {
if (ec == net::error::eof) {
ec = {};
}
if (ec)
return fail_http(ec, "shutdown");
}

static void get_server_time(net::io_context& ioc, ssl::context& ctx) {
static boost::url_view const uri{"https://api.binance.com/api/v3/time"};

std::make_shared<httpClient>(net::make_strand(ioc), ctx)
->run(uri, http::verb::get);
}
}} // namespace binapi::AsyncRest

int main() {
net::io_context ioc;

// The SSL context is required, and holds certificates
ssl::context ctx{ssl::context::tlsv12_client};

// Verify the remote server's certificate
ctx.set_verify_mode(ssl::verify_peer);
ctx.set_default_verify_paths();

binapi::AsyncRest::get_server_time(ioc, ctx);

ioc.run();
}

Now we know that res_ is beast::http::response<beast::http::string_body>. So, if you only want to print the body, print that:

    std::cout << res_.body() << std::endl;

Prints

Sending GET /api/v3/time HTTP/1.1
Host: api.binance.com
User-Agent: Boost.Beast/330

{"serverTime":1652476115413}
shutdown: stream truncated

To only print the time:

    static constexpr boost::gregorian::date s_epoch{1970, 1, 1};

auto epoch_seconds = json::parse(res_.body()).at("serverTime").as_int64();
ptime serverTime(s_epoch, boost::posix_time::milliseconds(epoch_seconds));

std::cout << serverTime << std::endl;

Prints

2022-May-13 21:38:55.982000

Summary/Notes

What it really looks like you're after is a function like

 ptime server_time();

Or

 void async_server_time(auto completionToken);

And you'd probably want to share the client class instance instead of reconnecting for each call.



Related Topics



Leave a reply



Submit