Skip to content
hpepper edited this page May 26, 2022 · 59 revisions

C++ notes

Introduction

References

std::list 👍 The add operation will instantiate a new MyClass, then copy in the data from the class instance that was to be added.

Inline code documentation

  1. doxywizard

Debugging with VS Code

Generel info

Tools

  • ar - archiving; creates .a files
    • Remove a .o file from a .a: ar d full.a something.o
    • Add a .o to a .a: ar r full.a new.o
  • c++filt - demangle c++ names
  • doxygen - Generate the documentation, extracted from the source code.
    • doxywizard - GUI for creating the doxygen.cfg
  • gdb - debugger
    • r - run
    • c - continue (after attach)
    • attach - attach to the process id: PID
    • bt - backtrace
  • nm - list symbol content
    • for boost.so files you have to use '--dynamic'

language

Inheritance

Keywords

  • explicit: all parameters has to match in order for the function to be used?
  • incline: TODO

Types

List

    m_listOfWalls.push_back(environmentWall);

    std::list<EnvironmentWall*>::iterator it;
    EnvironmentWall *pEnvironmentWall = nullptr;
    for (it = m_listOfWalls.begin(); it != m_listOfWalls.end(); ++it)
    {
        pEnvironmentWall = (*it);
        printf("DDD MapManager::renderEntities() startx: %d\n", pEnvironmentWall->getStartX());
        SDL_RenderCopy(renderer, m_wallTexture, m_wallSourceRectangle, &destinationRectangle);
    }

Map

#include <map>

std::map<std::string, SpriteInfo*> m_mapNameToSprite;

Itterate:

for (STRING_MAP::const_iterator it = story_design_map.cbegin(); it != story_design_map.cend(); ++it)
{
    send_dt(request, it->first);
    send_dd(request, it->second);
}
  • Extract and delete
   STRING_PAIR returnPair;
   STRING_MAP::iterator it;

    returnPair.first = key;
    returnPair.second = "";

    it = story_design_map->find(key);
    if (it != story_design_map->end())
    {
        returnPair.second = it->second;
        story_design_map->erase(it);
    }

Uncategorized operations

Command line parameters

std::string fileNameXml = "series.xml";

    const char *const short_opts = "hi:";
    const option long_opts[] = {
        {"seriesfile", required_argument, nullptr, 'i'},
        {"help", no_argument, nullptr, 'h'},
        {nullptr, no_argument, nullptr, 0}};

    int index;
    int opt = 0;
    while (opt != -1)
    {

        opt = getopt_long(argc, argv, short_opts, long_opts, &index);

        switch (opt)
        {
        case 'i':
            fileNameXml = optarg;
            break;
        }
    }

Makefile

Automatic variables

  • $@ - replaced with target.
  • $< - The name of the first prerequisite.
  • $? - The names of all the prerequisites that are newer than the target, with spaces between them.
    • If the target does not exist, all prerequisites will be included.
  • $^ - The names of all the prerequisites, with spaces between them.

memcheck

# check code quality
.PHONY: cppcheck memcheck
cppcheck:
	cppcheck --enable=all --language=c --std=c99 --inconclusive \
	--suppress=missingInclude $(SRC) -i $(INCLDIR)

memcheck: all
	valgrind -v --show-leak-kinds=all --leak-check=full --track-origins=yes \
	./$(BIN)

Unit Testing

cxxtest

File operations

Open file for write

#include <fstream> 
std::ofstream cFile;
cFile.open("myfile.txt");
cFile << "Test text line" << std::endl;
cFile.close();

Get base path

See: https://en.cppreference.com/w/cpp/filesystem/path

  std::filesystem::path pathForSeriesFile{fileNameXml};
  std::string directoryPathToSeriesFile = "./";
  if (pathForSeriesFile.has_parent_path())
  {
      directoryPathToSeriesFile = pathForSeriesFile.parent_path().string();
  }
  std::cout << "DDD path: " << directoryPathToSeriesFile << std::endl;
  • g++ -std=c++17 storystatus.cpp -o storystatus

XML File

Tiny XML

#include <tinyxml2.h>
#include <string>

int main()
{
    std::string fileNameXml = "series.xml";
    tinyxml2::XMLDocument *xmlDoc = new tinyxml2::XMLDocument();
    int nStatus = xmlDoc->LoadFile(fileNameXml.c_str());
    if (nStatus == 0)
    {
        tinyxml2::XMLElement *xmlRoot = xmlDoc->RootElement();
    }
}
  • g++ storystatus.cpp -o storystatus -ltinyxml2

TinyXml Generate list of SubElement content

	std::list<std::string> lDataList;

	tinyxml2::XMLElement *xmlChildElement = NULL;

	if (xmlElement != NULL) {
		xmlChildElement = xmlElement->FirstChildElement(sTagName.c_str());
	}
	// From https://stackoverflow.com/questions/7942191/how-to-handle-tinyxml-null-pointer-returned-on-gettext
	while (xmlChildElement != NULL) {

		// This is here to make sure there is valid data in the child element
		if (xmlChildElement->GetText() != NULL) {
			lDataList.push_back(xmlChildElement->GetText());
		} else {
			lDataList.push_back("");
		}
		xmlChildElement = xmlChildElement->NextSiblingElement(sTagName.c_str());
	}

	return (lDataList);

Network communication

UDP

Secure UDP communication

Secure UDP server

Secure UDP client

Creating a binary protocol

See:

  • https://gafferongames.com/post/reading_and_writing_packets/

  • Src code: https://github.com/gafferongames/protocol2

  • Text: takes up too much bandwith at 60pkgs per second

  • Structure serialization:

    • Endian conversion
    • Can't transfer pointers
    • Is a security risk if you just trust the package comming in on the network.
      • You must, at minimum do some sort of per-field checking that values are in range vs. blindly accepting what is sent to you.
        • This is why the memcpy struct approach is rarely used in professional games.

Serialize

Protocol decoding

Hex decoding

TCP

SSL

protobuf

  1. mkdir OUT_DIR
  2. protoc --cpp_out=OUT_DIR polyline.proto

WebSocket++

See: https://github.com/zaphoyd/websocketpp/issues/631

WebSocket++ includes two major object types. The endpoint and the connection.

The WebSocket++ endpoint

See: https://docs.websocketpp.org/md_tutorials_utility_client_utility_client.html

  • creates and launches new connections
  • maintains default settings for those connections.
  • manage any shared network resources.

WebSocket++ endpoints are built by combining an endpoint role with an endpoint config.

There are two different types of endpoint roles,

  • the client
  • server roles in a WebSocket session.

Endpoint Config:

WebSocket++ endpoints have a group of settings that may be configured at compile time via the config template parameter.

  • A config is a struct that contains types and static constants that are used to produce an endpoint with specific properties.
  • Depending on which config is being used the endpoint will have different methods available and may have additional third party dependencies.
  • Combine a config with an endpoint role to produce a fully configured endpoint. This type will be used frequently so I would recommend a typedef here.

typedef websocketpp::clientwebsocketpp::config::asio_client client

The WebSocket++ connection

  • stores information specific to each WebSocket session.

Once a connection is launched, there is no link between the endpoint and the connection.

  • All default settings are copied into the new connection by the endpoint.
  • Changing default settings on an endpoint will only affect future connections.

A new WebSocket connection is initiated via a three step process.

  1. a connection request is created by endpoint::get_connection(uri).
  2. the connection request is configured.
  3. the connection request is submitted back to the endpoint via endpoint::connect() which adds it to the queue of new connections to make.

WARNING: A connection_ptr allows direct access to information about the connection and allows changing connection settings. Because of this direct access and their internal resource management role within the library it is not safe for end applications to use connection_ptr except in the specific circumstances. See: https://docs.websocketpp.org/md_tutorials_utility_client_utility_client.html

WebSocket handlers

See:

Handlers can be registered at the endpoint level and at the connection level. Endpoint handlers are copied into new connections as they are created.

RegEx

Special:

  • . - any char
  • [] -
  • ? - 0 or 1
      • one or more
      • 0 or more
  • {} - match exactly this amount of times
  • \w - maybe works [a-zA-Z0-9]+
#include <string>
#include <regex>

std::string regexPattern = "\\w+\\.\\w+@\\w+\\.com";
std::regex regexRule(regexPattern);

std::string testString = "saldina@gmail.com";

bool isValidEmail = regex_match(testString, regexRule);

// Paretnthises must be in to be able to combine multiple rules
std::string lowercasePattern = "(?=.*[a-z])"
std::string uppercasePattern = "(?=.*[A-Z])"
std::string numberscasePattern = "(?=.*[0-9])"
std::string specialCharactersPattern = "(?=.*[$_@])"
std::string minCharactersn = "(?=.{6,})"

std:string fullPattern = lowercasePattern + uppercasePattern + numberscasePattern + specialCharactersPattern + minCharactersn;

std::string testStringPassword = "V3ryS3cret_";

std::regex regexPasswordRule(fullPattern);
// Use search because it is a number of rules combined, and not a single pattern.
bool isValidPassword = regex_search(testStringPassword, regexRule);
// see: https://www.youtube.com/watch?v=IOxKjqC1Ozo

#include <regex>
#include <iostream>

void dump_regex_matches(const std::cmatch &match)
{
    std::cout << "Matches:" << std::endl;
    for (std::size_t index = 0; index < match.size(); ++index)
    {
        std::cout << index << ": " << match.str(index) << std::endl;
    }
}

int main()
{
    // the '[^\]' means everything up until the closing square bracket.
    std::regex regex_pattern(R"((\d+): \[([^\]]+)\] (.*))");

    std::string testString = "123: [error] Hello World";
    std::cmatch match;
    std::regex_match(testString.c_str(), match, regex_pattern);

    dump_regex_matches(match);

}

Output:

Matches:
0: 123: [error] Hello World
1: 123
2: error
3: Hello World

C++ features

Lambda expressions

Syntax

  • [ captures ] ( params ) specifiers exception attr -> ret requires { body }
    • captures: aka lambda-introducer. makes sourounding vars available inside the body, either as references or value. From C++14 captures and also introduce new variables.
      • '&varname' by reference.
      • 'varname' by value.
      • '&' alone means default is to reference all non mentioned vars as reference.
      • '=' default get all by value.
      • Only variables that are mentioned in the lambda are captured when a capture-default is used.
        • If a capture clause includes a capture-default &, then no identifier in a capture of that capture clause can have the form & identifier.
        • Likewise, if the capture clause includes a capture-default =, then no capture of that capture clause can have the form = identifier.
        • An identifier or this cannot appear more than once in a capture clause.
      • A capture followed by an ellipsis is a pack expansion
  • params: A parameter list (lambda declarator in the Standard syntax) is optional and in most aspects resembles the parameter list for a function(MS).
  • specifiers: mutable specification Optional.
    • The mutable specification enables the body of a lambda expression to modify variables that are captured by value.
    • if n is param(copy by value). The mutable specification allows n to be modified within the lambda.
  • exception: optional 'throw()'
    • You can use the noexcept exception specification to indicate that the lambda expression does not throw any exceptions.
  • attr: ?
  • -> ret: trailing-return-type Optional. define the return type, can be auto.
    • The return type of a lambda expression is automatically deduced. You don't have to use the auto keyword unless you specify a trailing-return-type.
  • requires: ?
  • body:
    • The body of both an ordinary function and a lambda expression can access these kinds of variables:
      • Captured variables from the enclosing scope, as described previously.
      • Parameters
      • Locally-declared variables
      • Class data members, when declared inside a class and this is captured
      • Any variable that has static storage duration—for example, global variables

protobuff

auto (this should probably go into a deeper sub section)

These are my personal takes on 'auto'

  • Use auto for throw away things, like iterators.
    • Where it is a derived thing.
  • Do not use auto for primary definitions, it makes it harder for newcomers to read the code

Templates

  • This is a compiler abstraction.
  • When you invoke a template with a specific type the compiler creates a specialization of the template with that specific type.
    • Basically the compilers writes the templated code with the typenames replaced, e.g. replace 'T' with 'int'.
  • Using templates incur overhead both at compile time and run time.
  • Template classes er generally entirely defined in header files.
    • You can't have the definition and implementation of templates in separate .h and .cpp files, the linker wont be able to link them correctly.

See: 'C++ Templates and the STL' in LinkedIn learning.

  • use 'typename' or 'class' to define the 'TYPE' thingy
    • template
    • template T>
    • 'T' is an alias

WebAssembly

Troubleshooting

error: aggregate ‘std::ofstream cFile’ has incomplete type and cannot be defined

https://stackoverflow.com/questions/1057287/ofstream-error-in-c

Use: #include

Protobuf

google/protobuf/descriptor.proto: File not found.

Possibly missing protobuf-c or protobuf-dev

Clone this wiki locally