C++ Guide for EOS Development - Basics

Categories:

This post is part of my C++ Guide for EOS developers

  1. Basics
  2. Call by value / reference & Pointers
  3. Classes and Structs
  4. Templates
  5. Iterators & Lambda Expressions
  6. Multi-index
  7. Header files

Why C++?

The whole EOS blockchain infrastructure is written in C++. C++ is a low-level language that gives the programmer a lot of control over how you do things and manage your resources. The result is an immensely powerful and performant language being used a lot in performance critical applications like games, computer graphics, or on hardware with low resources like most embedded systems. However, shifting so much control to the developer also makes it one of the hardest languages to learn.

We need to learn C++ because your EOS smart contracts, the part of your decentralized application that lives on the blockchain, must also be written in C++. The C++ code is then compiled to WebAssembly. While theoretically, other “easier” languages can be compiled to WebAssembly (most notably RUST, Python, Solidity), C++ is the only officially supported language by Block One.

While these other languages may appear simpler, their performance will likely impact the scale of application you can build. We expect that C++ will be the best language for developing high-performance and secure smart contracts and plan to use C++ for the foreseeable future. EOS Developers Portal

Yes, C++ is scary and when your programming experience is mostly through high-level interpreted languages like JavaScript, it may seem daunting at first - but here’s the good news: Most of C++‘s features are actually not needed to write smart contracts. The intent of these tutorials will be to teach you the C++ basics and the advanced C++ features that you actually need for smart contract programming.

Let’s take a moment to acknowledge and hear about some useful modern C++ features that high-level languages like JavaScript don’t have. Most notably:

  • Statically typed (but comes with automatic type inference)
  • Preprocessor Macros
  • Explicit call-by-reference, call-by-value
  • Memory Pointers
  • Operator overloading
  • Generic programming through templates
  • typedefs

Don’t worry if you don’t understand these yet, we’ll start with the basics.

Basics

I assume you are already familiar with at least one programming language like JavaScript or Python. Then understanding the basics like defining variables, for loops, if conditionals or calling functions in C++ will also not be a surprise to you. Let’s take a look at the syntax:

// @url: https://repl.it/@MrToph/CPPBasics-1
// In c++ libraries are imported through the #include macro
// iostream comes with functions handling input and output to the console
#include <iostream>
// includes rand function
#include <cstdlib>
// includes time function
#include <ctime>

// this is how to define functions: <return type> <name>(<arguments>)
int compute(int x)
{
    // unsigned means no negative values which increases the range of numbers the variable can hold
    const unsigned int FIVE = 5;
    // FIVE = 3; // would throw an error as FIVE is declared constant
    return x * x + FIVE;
}

// the return type for no return value is called 'void'
void playGuessingGame()
{
    // initialize random number seed with time in seconds since the epoch
    srand(time(0));
    // get a random integer between 0 and 9 by doing modulus 10
    int random = std::rand() % 10;
    // need to initialize guess, uninitialized variables are indeterminate
    // meaning it could have any value, even the same as our random one!
    int guess = -1;
    std::cout << "Guess my number between 0 and 9";
    while (guess != random)
    {
        std::cin >> guess;
        if (guess > random)
            std::cout << "Lower";
        else if (guess < random)
            std::cout << "Higher";
        else
            std::cout << "Correct";
    }
}

// the entry point of your program is a function called main which returns an integer
int main()
{
    std::cout << "Hello! Type in a number\n";

    int number;
    std::cin >> number;

    int computed = compute(number);
    std::cout << "I computed x^2+5 as " << computed << "!\n";

    playGuessingGame();

    return 0;
}

There are many fundamental types for integer numbers short, int, long, long long (each with an unsigned alternative to represent non-negative integer numbers only). Their difference is in the number of bytes, and thus the range of integers, they hold. These types mentioned here do not have a specified size, their size is implementation-dependent. It could be that if you compile your program on one machine an int has 16 bits (sizeof(int) == 2), and when compiling on another machine it will have 32 bits. The only guarantee these types give you is a minimum number of bytes. For example, an int must have at least 16 bits, long at least 32 bits.

When working with numbers it’s helpful to know the exact ranges of the individual types, especially in security-sensitive applications like blockchain development where over-/underflows are critical. To address this, C99 added new types where you can explicitly ask for a specificly sized integer, for example int16_t, int32_t, or the unsigned uint64_t variant. When writing smart contracts, we will solely use these explicit fixed-size types.

Note that similar fixed-size types do not exist for floating point numbers as the number of bits doesn’t tell you very much about its precision and range. You’ll need to use float, double, long double in these cases (the former usually being 32 and 64 bit IEEE-754 floating point types).

Strings

Besides the number types and the boolean bool type, strings are one of the most used data types. They are included through <string> and live in the std namespace. (Namespaces are regions that scope variables and are a way to resolve name conflicts in bigger projects.)

// @url: https://repl.it/@MrToph/CPPBasics-Strings
#include <iostream>
// need to import <string> for strings
#include <string>

int main()
{
  // strings are part of the std namespace
  std::string text = "Hello";
  // + is used for concatenation
  text += " World";
  std::cout << text << "\n";
  // length and size are synonyms
  std::cout << text.length() << " " << text.size() << "\n";

  text = text.substr(0, 5);
  for (int i = 0; i < text.length(); i++)
  {
    std::cout << i << ": " << text[i] << "\n";
  }

  // different way to loop over characters
  for (char c : text)
  {
    std::cout << c << "\n";
  }
  return 0;
}

Arrays / Vectors

C++ differentiates between static and dynamic arrays. Static arrays are arrays with a fixed size which is known at compile time. If your arrays need to be able to grow or the size is only known at runtime, you need to use vectors.

// @url: https://repl.it/@MrToph/CPPBasics-Vectors
#include <iostream>
#include <vector>

int main()
{
    // arrays are defined with [] after the variable name
    // and can be immediately initialized providing values in { ... }
    int arr[] = {1, 2, 3};
    // can specify size in brackets
    // initializes elements in the list, rest to 0
    int brr[3] = {1, 3};
    for (int x : brr)
    {
        // outputs 1, 3, 0
        std::cout << x << "\n";
    }

    std::vector<int> numbers;
    for (int i = 0; i < 3; i++)
    {
        // add a number to the back
        numbers.emplace_back(i);
    }
    // size and accessing vectors is the same as with arrays
    std::cout << "numbers: " << numbers[0] << numbers.size() << "\n";

    // this inserts a number at the second (index 1) place
    numbers.emplace(numbers.begin() + 1, 9);
    // numbers.begin returns an iterator. More on these later
    for (std::vector<int>::iterator it = numbers.begin(); it != numbers.end(); it++)
    {
        // outputs 1, 3, 0
        std::cout << *it << "\n";
    }
}

Let’s leave it at this for now. In the next tutorial we will talk about different types to pass parameters to functions.

Learn EOS Development Signup

Hi, I'm Christoph Michel 👋

I'm a , , and .

Currently, I mostly work in software security and do on an independent contractor basis.

I strive for efficiency and therefore track many aspects of my life.