4
\$\begingroup\$

I am trying to create my own algorithmic trading system using C++. I have searched the web for a nice tutorial for such systems and I didn't find any. Then I started to learn about curl and jsoncpp libraries and even documentation of those libraries are not made for a novice programmer like me. I mostly reused most of the code for creating this scraper especially WriteCallback function which I directly copy pasted from other C++ code without understanding it. I am sharing this whole code because someone else doesn't need to reinvent the wheel and can use this code as a template. Anyway this code is working properly (Ubuntu 14.04, CodeBlocks IDE, API: fixer.io) and any kind of suggestion or feedback is welcomed.

#include <string.h>
#include <iostream>
#include <stdio.h>
#include <jsoncpp/json/json.h>
#include <curl/curl.h>

//writing call back function for storing fetched values in memory
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
    ((std::string*)userp)->append((char*)contents, size * nmemb);
    return size * nmemb;
}


int main(void)
{
  std::string readBuffer;


 //global initiliation of curl before calling a function
 curl_global_init( CURL_GLOBAL_ALL );

 //creating session handle
 CURL * myHandle;

 // We’ll store the result of CURL’s webpage retrieval, for simple error checking.
 CURLcode result;


 // notice the lack of major error-checking, for brevity
 myHandle = curl_easy_init ( ) ;

 //after creating handle we ill start transfering webpage

 //curl_easy_setopt is used to tell libcurl how to behave.
 //By setting the appropriate options, the application can change libcurl's behavior.

 //CURLOPT_URL provide the URL to use in the request. Pass in a pointer to the URL to work with.
 //sample json output >> {"base":"EUR","date":"2016-07-22","rates":{"GBP":0.84108,"USD":1.1014}}
 curl_easy_setopt(myHandle, CURLOPT_URL, "http://api.fixer.io/latest?symbols=USD,GBP");

   /* send all data to this function  */
  curl_easy_setopt(myHandle, CURLOPT_WRITEFUNCTION, WriteCallback);

   /* we pass our 'chunk' struct to the callback function */
  curl_easy_setopt(myHandle, CURLOPT_WRITEDATA, &readBuffer);

 //perform a blocking file transfer
 result = curl_easy_perform( myHandle );

 /* check for errors */
  if(result != CURLE_OK) {
    fprintf(stderr, "curl_easy_perform() failed: %s\n",
            curl_easy_strerror(result));
  }
  else {
    /*
     * Now, our chunk.memory points to a memory block that is chunk.size
     * bytes big and contains the remote file.
     *
     * Do something nice with it!
     */

    //std::cout << readBuffer << std::endl;

    //json parsing
Json::Value root;   // will contains the root value after parsing.
Json::Reader reader;
bool parsingSuccessful = reader.parse( readBuffer, root );
if(not parsingSuccessful)
 {
   // Report failures and their locations
   // in the document.
   std::cout<<"Failed to parse JSON"<<std::endl
       <<reader.getFormatedErrorMessages()
       <<std::endl;
   return 1;
 }else{
 std::cout<<"\nSucess parsing json\n"<<std::endl;
 std::cout << root<< std::endl;
 std::cout <<"Base = "<< root["base"].asString() << std::endl;
 std::cout <<"Date = "<< root["date"].asString() << std::endl;
 std::cout <<"Rate GBP ="<< root["rates"]["GBP"].asFloat() << std::endl;
 std::cout <<"Rate USD ="<< root["rates"]["USD"].asFloat() << std::endl;

 }

  }




 //End a libcurl easy handle.This function must be the last function to call for an easy session
 curl_easy_cleanup( myHandle );


 return 0;
}
\$\endgroup\$
6
  • 1
    \$\begingroup\$ I have some reviews that may help: 1) Convert Json From a stream into an object: Json Serializer 2) Treat a socket like a stream Socket Stream I have included simple instructions to set it up here: gist.github.com/martin-seomoz/723b790fdd8ac1d5b1b3eebd42124723 \$\endgroup\$ Jul 24, 2016 at 17:24
  • \$\begingroup\$ Can you please explain the advantage of using ThorSocketStream library. \$\endgroup\$
    – Eka
    Jul 25, 2016 at 1:36
  • 1
    \$\begingroup\$ Hello! Please don't make changes to the original post once it has been reviewed, as that invalidates the current answers. Please see our meta side on performing iterative reviews for more information! \$\endgroup\$
    – syb0rg
    Jul 28, 2016 at 16:40
  • \$\begingroup\$ @syb0rg What is the best way to add an updated code? Should I use answer post itself? should i ask this question in meta? \$\endgroup\$
    – Eka
    Jul 28, 2016 at 16:44
  • \$\begingroup\$ @Eka Read the meta post, and decide for yourself based on the options given \$\endgroup\$
    – syb0rg
    Jul 28, 2016 at 16:54

1 Answer 1

2
\$\begingroup\$

These are C headers

#include <string.h>
#include <stdio.h>

You should probably use the C++ version (I hope you are not using C-String anywhere).

#include <strings>
#include <cstdio>

Header include paths

#include <jsoncpp/json/json.h>

This should probably be json/json.h and you should specify the location where jsoncpp is installed with compiler flags.

Curl is a C library.

Therefore any callbacks should be C functions (not C++ functions). There is no guarantee in the standard thay they use the same ABI.

static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)

Should be:

extern "C" size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)

Never use C-Casts

C casts are hard to spot (in general). But also using C++ casts you can mark dangerious casts by uisng the appropriate cast.

    ((std::string*)userp)->append((char*)contents, size * nmemb);

I would use reinterpret_cast<> here. So that it is easy to spot and you intuitively know that this is a dangerous operation.

reinterpret_cast<std::string*>(userp)->append(static_cast<char*>(contents), size * nmemb);

Always check error codes.

All the following return an error code. You should validate these functions worked by checking this error code.

 curl_global_init( CURL_GLOBAL_ALL );
 curl_easy_setopt(myHandle, CURLOPT_URL, "http://api.fixer.io/latest?symbols=USD,GBP");
 curl_easy_setopt(myHandle, CURLOPT_WRITEFUNCTION, WriteCallback);
 curl_easy_setopt(myHandle, CURLOPT_WRITEDATA, &readBuffer);

Prefer "\n" over std::endl

 std::cout<<"\nSucess parsing json\n"<<std::endl;
 std::cout << root<< std::endl;
 std::cout <<"Base = "<< root["base"].asString() << std::endl;
 std::cout <<"Date = "<< root["date"].asString() << std::endl;
 std::cout <<"Rate GBP ="<< root["rates"]["GBP"].asFloat() << std::endl;
 std::cout <<"Rate USD ="<< root["rates"]["USD"].asFloat() << std::endl;

The difference is that std::endl also calls std::flush on the stream. There is no need to flush the stream manually. The stream is designed to flush when it needs too. When you do it manually you will make your code much more eneficent.

RAII

 myHandle = curl_easy_init ( ) ;
 // STUFF
 curl_easy_cleanup( myHandle );

When you see this pattern. You should be thinking of using RAII. This will make sure your resources are cleaned up correctly in all situations (including when exceptions are thrown) are the function returns early (aka return 1;).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.