Aby wykonac to zadanie musialem zapoznac sie z wieloma nowymi dla mnie informacjami. Wiecej czytalem (dokumentacja, kod zrodlowy bibliotek, informacje o http, emscripten) niz kodowalem lub debugowalem. Rezultat :
Kompilacja 1go pliku:

CXX=g++ 
CPPFLAGS=-I/home/mat/asio-1.18.2/include  -I/home/mat/json/include -Wall -std=c++14 -fcompare-debug-second
LIBS=-lpthread 

all: asio_test

asio_test: asio_test.cpp
	$(CXX) -o $@ $^ $(CPPFLAGS)  $(LIBS)
	
clean :
	rm -f *.o asio_test
//#define ASIO_STANDALONE 
#include "asio.hpp"
#include <iostream>
#include <sstream>
#include <utility>
#include <string>
#include <map>    
#include <array> 
//#define NDEBUG
#include <cassert>
#include <tuple>
#include <chrono>

#include "nlohmann/json.hpp"

using namespace std;

template<class Type>
inline bool is_valid(const int INDEX, const Type& object) {
   static constexpr size_t SIZE = tuple_size<Type>{};
   if (INDEX < 0 || INDEX >= SIZE) {
      assert(0 && "Unavailable index ");
      return false;
   }
   return true;
}

enum class Cache_control { no_store, no_cache };
ostream& operator<<(ostream& os, const Cache_control& x) {
   static const array<string, 2> AVAILABLE_CACHE_CONTROLS = { "no-store", "no-cache" };
   const int INDEX = static_cast<int>(x);
   if (is_valid(INDEX, AVAILABLE_CACHE_CONTROLS))
      return os << AVAILABLE_CACHE_CONTROLS[INDEX];
   return os << AVAILABLE_CACHE_CONTROLS[0];
}

enum class Connection { keep_alive, close };
ostream& operator<<(ostream& os, const Connection& x) {
   static const array<string, 2> AVAILABLE_CONNECTIONS = { "keep-alive", "close" };
   const int INDEX = static_cast<int>(x);
   if (is_valid(INDEX, AVAILABLE_CONNECTIONS))
      return os << AVAILABLE_CONNECTIONS[INDEX];
   return os << AVAILABLE_CONNECTIONS[1];
}

enum class Method { get, options, post };
ostream& operator<<(ostream& os, const Method& x) {
   static const array<string, 3> AVAILABLE_METHODS = { "GET", "OPTIONS", "POST" };
   const int INDEX = static_cast<int>(x);
   if (is_valid(INDEX, AVAILABLE_METHODS))
      return os << AVAILABLE_METHODS[INDEX];
   return os << AVAILABLE_METHODS[0];
}

void process_document(const string & CURRENCY, const string & DOC);
string get_document(const string & HOST, const Method & METHOD, const string & DIRECTORY, 
                         const Cache_control & CACHE_CONTROL, const Connection & CONNECTION);

class Asio_IO_Stream_Exception : public exception { 
   string msg {"!!! error on the socket stream: "};
public:
   Asio_IO_Stream_Exception() {}
   Asio_IO_Stream_Exception(const string& message) { msg += message; }
   const char* what() const noexcept {
      return msg.c_str();
   }
};

int main() {
   try {
      const string CURRENCY = "PLN";
      const string HOST = "www.floatrates.com";
      const Method METHOD = Method::get;
      const string DIRECTORY = "/daily/" + CURRENCY + ".json";
      //const string DIRECTORY = "/";            // "/" is root (main page of host) and "" has result 400 Bad Request
      const Cache_control CACHE_CONTROL = Cache_control::no_store;
      const Connection CONNECTION = Connection::close;
      const string DOC = get_document(HOST, METHOD, DIRECTORY, CACHE_CONTROL, CONNECTION);
      process_document(CURRENCY, DOC);
      return 0;
   }
   catch (const nlohmann::json::exception & e) {
      cerr << "!!! Error json exception: " << e.what() << '\n';
   }
   catch (const Asio_IO_Stream_Exception & e) {
      cerr << e.what() << endl;
   }
   catch (const asio::system_error &e) {
      cerr << "!!! System Error ! Error code = " << e.code()
           << "\n Message: " << e.what();
      return e.code().value();
   }
   catch (const exception & e) {
      cerr << "Exception: " << e.what() << endl;
   }
   catch (...) {
      cerr << "Unrecognized Exception: " <<  endl;
   }
   return 1;
}

struct float_rates_info {
   string code;
   double rate;
   double inverse_rate;
};

inline void from_json(const nlohmann::json& json_data, float_rates_info& info) {
   info.code = json_data.at("code").get<string>();
   info.rate = json_data.at("rate").get<double>();
   info.inverse_rate = json_data.at("inverseRate").get<double>();
}

void modify_document(string & doc);
void process_response_headers(asio::ip::tcp::iostream & socket_iostream, const string& HTTP_VERSION);

string get_document(const string & HOST, const Method & METHOD, const string & DIRECTORY,
                         const Cache_control & CACHE_CONTROL, const Connection & CONNECTION) {
   asio::basic_socket_iostream<asio::ip::tcp> socket_iostream;
   const string HTTP_VERSION("HTTP/1.1");
   socket_iostream.connect(HOST, "http");
   socket_iostream.expires_from_now(chrono::seconds(10));
   socket_iostream    << METHOD << " " << DIRECTORY << " " << HTTP_VERSION << "\r\n";
   socket_iostream    << "Host: " + HOST + "\r\n";
   socket_iostream    << "Cache-Control: " << CACHE_CONTROL << "\r\n";
   socket_iostream    << "Connection: " << CONNECTION  << "\r\n\r\n";
   socket_iostream.flush();
   
   process_response_headers(socket_iostream, HTTP_VERSION);
   
   stringstream strstream;
   strstream << socket_iostream.rdbuf();
   
   socket_iostream.close();
   if (! socket_iostream)
      throw Asio_IO_Stream_Exception(socket_iostream.error().message());
   string result = strstream.str();
   modify_document(result);
   return result;
}

void process_response_headers(asio::ip::tcp::iostream & socket_iostream, const string& HTTP_VERSION) {
   string http_version;
   socket_iostream >> http_version;
   cout << " http_version = " << http_version;
   unsigned int status_code;
   socket_iostream >> status_code;
   cout << "\n status code of response = " << status_code;
   string status_message;
   getline(socket_iostream, status_message);
   cout << "\n status message = " << status_message;
   if (socket_iostream.fail() || http_version != HTTP_VERSION) 
      throw Asio_IO_Stream_Exception("Invalid response ");
   // Process the response headers, which are terminated by a blank line.
   string header;
   while (getline(socket_iostream, header) && header != "\r")
      cout << header << "\n";
   cout << "\n";
}

void process_document(const string & CURRENCY, const string & DOC) {
   stringstream strstream;
   strstream.str(DOC);
   
   nlohmann::json json_data;
   strstream >> json_data;

   map<string, float_rates_info> rates = json_data;
   for (const pair<string, float_rates_info> &p : rates) 
      cout << " 1 " << CURRENCY << " = " << p.second.rate << " " << p.second.code << " and "
           << " 1 " << p.second.code << " = " << p.second.inverse_rate << " " << CURRENCY << endl;
}

void erase(string & result, const char C, const char A) {
   for (unsigned i = 0; i < result.size(); i++) {
      if (C == result[i] || A == result[i]) {
         result.erase(result.begin() + i);
         if (A == result[i]) {
            result.erase(result.begin() + i);
            while (i < result.size() && result[i] != A) 
               result.erase(result.begin() + i);
            if (i < result.size() && A == result[i]) 
               result.erase(result.begin() + i);
         }
      }
   }
}

void modify_document(string & doc) {
   size_t first = doc.find("{");
   doc = doc.substr(first);
   size_t last = doc.rfind("}");
   doc = doc.substr(0, last + 1);
   erase(doc, '\r', '\n');
}

Kompilacja 2go pliku:

CXX=g++ 

LIBXMLNAME = pugixml
LIBXML = ${LIBXMLNAME}/lib${LIBXMLNAME}.a
LIBXML_HEADER_DIR = /home/mat/pugixml-1.11/src
CPPFLAGS= -Wall -std=c++14 -fcompare-debug-second
EXEC = curlcpp_test

LPATH=-L/home/mat/curlcpp/build/src -L${LIBXMLNAME}
LIBS=-lcurlcpp -lcurl -l${LIBXMLNAME}

build: directories ${EXEC}.o ${LIBXML}
	${CXX} ${EXEC}.o ${LPATH} ${LIBS}  -o ${EXEC}

directories:
	mkdir -p ${LIBXMLNAME}

${LIBXML}: ${LIBXMLNAME}/${LIBXMLNAME}.o
	ar rcs ${LIBXML} ${LIBXMLNAME}/${LIBXMLNAME}.o

${LIBXMLNAME}/${LIBXMLNAME}.o: ${LIBXML_HEADER_DIR}/${LIBXMLNAME}.cpp
	${CXX} -c $(CPPFLAGS) -I${LIBXML_HEADER_DIR}  $< -o $@

%.o: %.cpp 
	${CXX} -c $(CPPFLAGS) -I${LIBXML_HEADER_DIR} -I/home/mat/curlcpp/include  $< -o $@

clean: 
	rm -rf ${LIBXMLNAME} *.o  ${EXEC}


#include <iostream>
#include <string>
#include <array>

#include "curl_header.h"
#include "curl_easy.h"
#include "curl_exception.h"
#include "curl_ios.h"
#include "pugixml.hpp"

using namespace curl;
using namespace pugi;
using namespace std; 

bool process_xml_document(const char * STR);
string get_xml_document(const char* URL);

int main() {
   try {
      static const array<const char*, 2> URL_ARRAY = { "http://api.nbp.pl/api/exchangerates/tables/a/",
                                                       "http://api.nbp.pl/api/exchangerates/tables/b/" };
      string xml_doc;
      for (const char* URL :  URL_ARRAY) {
         xml_doc = get_xml_document(URL);
         if (xml_doc.empty()) {
            cerr << "Error: Xml document can not be empty\n";
            return 1;
         }
         if (! process_xml_document(xml_doc.c_str()))
            return 1;
      }
      return 0;
   }
   catch (const exception & e) {
      cerr << "Exception: " << e.what() << endl;
   }
   catch (...) {
      cerr << "Unrecognized Exception: " <<  endl;
   }
   return 1;
}

stringstream get_document(const char* URL, const curl_header & HEADER) {
   stringstream stream;
   try {
      curl_ios<stringstream> writer(stream);
      curl_easy easy(writer);
      
      easy.add<CURLOPT_HTTPHEADER>(HEADER.get());
      easy.add<CURLOPT_URL>(URL);
      easy.add<CURLOPT_TIMEOUT>(10);
      easy.add<CURLOPT_DNS_CACHE_TIMEOUT>(0);
      easy.add<CURLOPT_FOLLOWLOCATION>(1);

      easy.perform();
   }
   catch (const curl_easy_exception & e) {
      curlcpp_traceback errors = e.get_traceback();
      e.print_traceback();
   }
   return stream;
}

string get_xml_document(const char* URL) {
   curl_header header;
   header.add("Accept: application/xml");
   stringstream stream = get_document(URL, header);
   return stream.str();
}

inline bool load_validate_xml(xml_document& xml_doc, const char * STR) {
   xml_parse_result result = xml_doc.load_string(STR);
   if (result.status != xml_parse_status::status_ok)  {
      cerr << "XML [" << STR << "] parsed with errors, attr value: [" 
         << xml_doc.child("node").attribute("attr").value() << "]\n";
      cerr << "Error description: " << result.description() << "\n";
      cerr << "Error offset: " << result.offset << " (error at [..." << STR[result.offset] << "]\n\n";
   }
   return result;
}

bool print(const xpath_node_set & CURRENCIES, const xpath_node_set & CODES, const xpath_node_set & RATES) {
   const size_t SIZE = CURRENCIES.size();
   if (SIZE != CODES.size() || SIZE != RATES.size()) {
      cerr << "Different size of xpath node sets:\n";
      cerr << "CURRENCIES = " << SIZE << "\n";
      cerr << "CODES = " << CODES.size() << "\n";
      cerr << "RATES = " << RATES.size() << "\n";
      return false;
   }
   for (size_t i = 0; i < SIZE; i++) {
      xpath_node  code_xpathnode = CODES[i];
      xml_node    code_xmlnode   = code_xpathnode.node();
      xml_text    code_xmltext   = code_xmlnode.text();
      xpath_node  rate_xpathnode = RATES[i];
      xml_node    rate_xmlnode   = rate_xpathnode.node();
      xml_text    rate_xmltext   = rate_xmlnode.text();
      double rate                = stod(rate_xmltext.as_string());
      double inverse_rate        = 1.0 / rate;
      cout << " 1 PLN = " << inverse_rate << " " << code_xmltext.get() << " and"
           << " 1 " << code_xmltext.as_string() << " = " << rate << " PLN\n";
   }
   return true;
}

bool process_xml_document(const char * STR) {
   xml_document doc;
   if (! load_validate_xml(doc, STR))
      return false;
   try {
      static constexpr char* CURRENCY_NODE = "/ArrayOfExchangeRatesTable/ExchangeRatesTable/Rates/Rate/Currency";
      static constexpr char* CODE_NODE = "/ArrayOfExchangeRatesTable/ExchangeRatesTable/Rates/Rate/Code";
      static constexpr char* MID_RATE_NODE = "/ArrayOfExchangeRatesTable/ExchangeRatesTable/Rates/Rate/Mid";
      const xpath_node_set CURRENCIES = doc.select_nodes(CURRENCY_NODE);
      const xpath_node_set CODES = doc.select_nodes(CODE_NODE);
      const xpath_node_set RATES = doc.select_nodes(MID_RATE_NODE);
      return print(CURRENCIES, CODES, RATES);
   }
   catch (xpath_exception const & e) {
      cerr << e.result().description() << endl;
      return false;
   }
}