Just a curious programmer.
2997 words

Daily Dose C++: std::endl

Source:

C++ Weekly: Stop using std::endl
CPP Reference endl
CPP Reference flush

The Dose:

<< std::endl; is the same as << "\n" << std::flush;.

Do you need to flush every time you end a string?

I definitely don't have to.

Better to just call flush when you need it.

MariaDB C++ Connector Setup for MacOS

For some reason, when I was setting this up, I was having a hard time getting it to work. Save myself the hassle next time by having step by step notes on what I did to get it working. Also has the added benefit of helping someone else if they ever stumble on this.

Install MariaDB using brew

brew install mariadb
This is to get the mariadb C connector which is packaged with the full release/build of mariadb server.
After installing it, you don't have to start up the server if you don't need to. I'm personally hosting an instance through docker.

Setup your project

Going to reference this as helloMaria.

Let's assume you to create your project folder

mkdir helloMaria && cd helloMaria

# Don't really need to git init this in here
#   but it's just to illustrate the usage of git submodule
git init

# CMakeLists for building.
touch CMakeLists.txt

I'm using CLion, so the project directory is already created with a CMakeLists.txt already.

Get MariaDB C++ bindings

Note: I think I had problems last time because I didn't install the full MariaDB. The C++ bindings attach to the mariaDB C connector.

So within your project's folder, do a git clone to the project and follow the instructions according viaduck's project.

# assuming your are in the helloMaria dir
git clone https://github.com/viaduck/mariadbpp.git
cd mariadbpp
git submodule update --init
# now head back to the root project folder
cd ..

This is for the local version. The project Readme outlines a way to build and install for system-wide usage.

Set up cmake

Set it up by modifying CMakeLists. I'm using CLion so it creates the file for me already.

# assuming your are in the helloMaria dir
nano CMakeLists.txt

The CMake I used for MacOS is:

cmake_minimum_required(VERSION 3.17)
project(helloMaria)

set(CMAKE_CXX_STANDARD 20)
add_subdirectory(./mariadbpp)

set(SOURCES main.cpp)
add_executable(${PROJECT_NAME} ${SOURCES})

target_link_libraries(${PROJECT_NAME} mariadbclientpp)

Build and Run helloMaria project to test.


#include <iostream>
#include <string>
#include <mariadb++/connection.hpp>

int main() {

    // set up the account
    mariadb::account_ref acc = mariadb::account::create(
            "127.0.0.1", "username_string", "password_string",
            "hello", 3306, "");
        //  Note: the arguments in order (host_name, user_name, password, schema(database), port, unix_socket)

    // create connection
    mariadb::connection_ref con = mariadb::connection::create(acc);

    // create statement object
    mariadb::statement_ref stmt = con->create_statement("SELECT * FROM versionTable;");
    mariadb::result_set_ref result = stmt->query();

    // reading from a result set
    while (result->next()) {
        std::string a = result->get_string(0);
        std::string b = result->get_string("scriptVersion");

        std::cout << a << " " << b << std::endl;
    }

    return 0;
}


And that's how I got it to work.

GTK3, C++, CMake, And CLion

Why?

All of the libraries and tools in the title are cross platform.
Also wanted to learn GTK3 so I can develop some apps for the Librem 5.
Work requires I build software for Windows and Mac, though, so cross platform is a must.
Going to outline steps needed to get started with Mac, Linux (Fedora), and Windows.

TL;DR

Install the gtk libraries for the GTK3 C++ bindings.
Mac: brew install gtkmm3
Fedora Linux: sudo dnf install gtkmm30 gtkmm30-devel gtkmm30-doc

CLion Cmake (Confirmed to work on Mac and Fedora Linux).

cmake_minimum_required(VERSION 3.17)

set(CMAKE_CXX_STANDARD 20)

project(hellogtk)
set(SOURCE_FILES main.cpp helloworld.cpp helloworld.h)

# use this to find the brew package.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTKMM REQUIRED gtkmm-3.0)

# Available variables: GTKMM_INCLUDE_DIRS, GTKMM_LIBRARY_DIRS and GTKMM_LIBRARIES

include_directories(${GTKMM_INCLUDE_DIRS})
link_directories(${GTKMM_LIBRARY_DIRS})

add_executable(${PROJECT_NAME} ${SOURCE_FILES})
target_link_libraries(${PROJECT_NAME} ${GTKMM_LIBRARIES})

# Been warned that order in cmake matters.

How

Assuming I have CLion installed. Cmake should be bundled with CLion since it's a core component in how their IntelliSense features function.

Install gtkmm3 with a package manager. gtkmm is the c++ bindings for gtk, and the 3 denotes that we are using the GTK3 version.
Used brew for MacOS and dnf for Fedora.

Pretty sure brew installs all the dependencies.
Ran into an issue on Fedora where cmake couldn't find gtkmm3 packages. Either resolved by installing gtkmm30-devel and/or resetting the cmake cache. Did both for good measure.

Using this wiki page as reference on how to setup the Cmake Above.

Just used the given HelloWorld code

Suggested by the docs that we split the source files into a separate folder.
Seems better that way, but for the Cmake above, everything is in the project root folder.

Notes

Works with CLion toolchains:

  • GCC (my default):
    • gmake
    • gcc-10
    • g++-10
    • Bundled CMake and GDB
  • Xcode Toolchain:
    • gmake
    • Xcode C Compiler
    • Xcode C++ Compiler
    • Bundled CMake and LLDB

for some reason, upon close in MacOS, GCC compiled version will throw a malloc error.

hellogtk(14953,0x1195d8dc0) malloc: *** error for object 0x7ffee5519a00: pointer being freed was not allocated
hellogtk(14953,0x1195d8dc0) malloc: *** set a breakpoint in malloc_error_break to debug

Clang is fine though.

Daily Dose C++: Vector Copy

Note

Been keeping busy, missed out on a month of C++ Dosing.

  • NVIDIA GTC a few weeks ago
  • Going through the CPP Con talks
  • Fast.AI video tutorial/book read
  • Learning CMake and GUI Frameworks. (going with GTK)

Maybe I should just reduce the scope of the daily dosage. But I digress.

Taking a Data structures in C++ class for review/fun. Ran into a problem where we wanted to copy a vector object into a new one.

The Dose

Iterate and Copy

// Lambda to do the copy:
auto vectorCopy = [](std::vector<int> vIn) -> std::vector<int>
{
    std::vector<int> vOut;
    for (auto v: vIn)
        vOut.emplace_back(v);
    return vOut;
};

// Usage:
std::vector<int> copyTo = vectorCopy(copyFrom);

Easy to implement, but thought, there has to be a better way!

std::copy

std::copy(copyFrom.begin(), copyFrom.end(), back_inserter(copyTo));

Thought this was straightforward, but wasn't as easy as I thought.
You need to use the back_inserter as the third argument.

Why? I'm not too sure myself. What other use cases are there?

Copy During Initialization

std::vector<int> copyFrom = {1, 2};
std::vector<int> copyTo(copyFrom);

Now this is pretty elegant. Pass in another vector object and it will create the copy during initialization. This is the one we ultimately went with.

Daily Dose C++: Lambdas (Part 2)

Source

Back To Basics: Lambda Expressions - Barbara Geller & Ansel Sermersheim - CppCon 2020

Starting from 31:50

C++20 Update

Lambda Expression Format:
[capture clause] <template parameters> (parameter list) specifier exception attribute -> return type requires {body}

  • specifier keyword:

    • mutable (C++11)
    • constexpr (C++17)
      • Deduced so keyword is optional.
      • Only reason, lambda express NEEDS to be constexpr.
      • Can constexpr be using in a lambda expression?
        • Yes, only if lambda is a generic type. Only in the body.
    • consteval (C++20)
      • similar to how const expr does in lexp
  • exception keyword:

    • noexcept: cannot throw an exception if noexcept keyword used in the exception spot of the l-expression format above.
  • attribute keyword:

    • Function attributes are not supported in lambda expressions.
      • nodiscard, deprecated, noreturn
    • Function type appears at the end of declaration, gnu::cdecl, gnu::regcall.
      • This is compiler specific.
  • requires keyword:

    • Lambda Expressions need to be generic
    • e.g. requires std::copyable\<T>

What's so great?

  • Easier to read
  • Convenient to write than a function object
  • Invoked immediately and not saved to a variable
  • Easier to use with algorithms and std::visit(), std::thread.
  • Designed well to work with those items.
  • Instead of writing a whole class and code, you just write the important parts and the compilers takes care of the rest.
    • Lambda expressions described as kind of backwards.

Callback

  • Can be passed as a function pointer, f obj, or closure.
  • Callback will be invoked by the receiver.
// Callback with STL Algorithms : std::count_if
int resB = std::count_if(data.begin(), data.end(), [](int i){return i>5;});

Maps

auto myLamb = [](const std::string &a, const std::string &b)
    {return a.size() > b.size();};

std::map<std::string, int, decltype(myLamb)> myMapB =
    {{string name, int size}, {"e.g.NameHere", 10}, ...}

Have to define as decltype and passed lambda expression as an auto decltype.

Generic Lambda

If auto is used for parameters declaration is what defines a generic lambda.
At least 1 parameter is defined as auto, then the whole lambda expression is generic.

auto myLamb = [](){} This auto is to define the closure type, not the auto defining the generic.

datatype myLamb = [](auto var1, int var2){} This auto is the one that defines a generic.

Capturing Structured Bindings

auto [x, y] = someFunction(); // Structured Bindings
auto myLamb = [x](){return x+7;} // Capturing x from Structured Binding

Technically illegal C++17. But some compilers will allow it.
If it fails to build, work around is to just set the capture clause as [x = x].

Resolved in C++20, but some compilers might not be up to date with this fix.

Extra Notes

  • No recursive lambdas, can't call itself.

  • C++11 Lambdas is more of a beta release. Preferred to be used in at least C++14.

  • Function pointers are more expensive to call than function object.

  • Beneficial if you capture the std::move(myPtr) and place it into a variable of a different name.

    • I'm guessing this is to reduce confusion. Since the value will no longer exist in the old variable.
    • Common 'gotcha' you need to be wary of.
  • Also related to the move action, be careful about mutable lambdas.

    • Try to use movable captures over mutable lambda expressions.

Daily C++ Dose: Lambdas (Part 1)

Was reading a blog post about lambdas, and found myself asking what a lambda was really. Then saw there was a great "Back to Basics" presentation on lambdas, so decided to just go through that first. There is a lot to learn about lambdas, so going to break it into multiple posts over multiple days.

Source

Back To Basics: Lambda Expressions - Barbara Geller & Ansel Sermersheim - CppCon 2020

Up to 31:50

Notes

Structure for Lambdas: [](){}

[capture](parameters) -> returntype{body}

Can omit (), but would look weird. So best practice to just keep it.

Added in C++11 but constantly being updated with each new standard.

Return type should always be auto because return type is compiler dependent.

Parameters list: Can be empty or omitted, but highly recommended to keep it there even if it is empty.

Return type: Data type of return item in body, if it can't be deduced.

Body: contains the programming statements and can't be empty

Background

  • Lambda expression is an expression that returns a function object.
  • Some background. function pointer is a data type.
  • Set's up to a point to a function, but you don't de-reference it with a '*'.
void (*myProcess)(int);
myProcess = std::exit;
myProcess(42);

// myProcess looks like a function. notice how it is not *myProcess.

Everything to the right of the equal size is the lambda expression: auto myLamb = [](){ return 17; };

Once the lambda is evaluated, it becomes a closure. What is closure in C++? Term for any function object that is returned from a lambda expression.

Operator Overloading examples:

bool operator==(const T &value)
bool operator>(const T &value)
T operator+=(const T &value)
void operator()()
bool operator()(int value)
double operator()(double d1, double d2)

Terms

  • Function Object Data Type

    • Class or structure with a function call operator method.
    • Class which declares the operator()() method.
  • Function object

    • Instance of a function object data type.
    • It is a callable object.
class Pclass{...}

Pclass pc;
pc.operator()("method 1");
pc("method 2");

//notice how the syntax for method 2 is the same format for calling a 
//function, function pointer or a function object.
  • Avoid using the term Functor

    • most programmers use the term Functor when discussing function objects.
    • best case to use standard terms outlined by the standard.
    • Functor has it's own meaning, so only use Functor if you actually mean it.
      • TODO: add Functor exact defintion.
  • std::function

    • Also added in C++11.
    • Wanted a class to store a function object.
    • Allows to pass a lambda function to another function.

Lambda Captures

Capture clause

Which variables are visible in the body.

Required to be there, even if it is empty.

Variables listed here are captured from the outer scope.

  • capture by value

    • Value is copied as a const value.
    • either local scope or this->
    • variable doesn't matter if out of scope when lambdas is called.
    • Any number of variables in capture clause.
    • Modify captured value? mark entire lambda as mutable. will capture by value. e.g. auto myLamb = [x]() mutable { return ++x; };
  • capture by reference

    • An & is added to indicate capture by lvalue reference.
  • Don't need to capture global variables, already accessible.

Important Note: Capture by Reference

If capture variable is defined prior to lambda, variable will be resolved to that value, even if the variable is modified after the lambda.

If you capture by reference (eg. &x instead of x). then the value of x at the time of access will be used. but beware because it is an address, will be undefined behavior if x is out of scope before invoking the lambda.

C++14 - Generalized Capture

  • By Value
    • [varA = 10] or [varB = x], will save a copy value into assignment
  • By Reference
    • [&varC = y], y must be declared in local scope.
  • By Move
    • [varD = std::move(z)], move occurs where the lambda express is defined.

Capture clause

C++11: [this] capture pointer by value

C++14: [self = *this] capture object by value and initialize new variable

C++17": [*this] capture *this object by value.

Beware of the following

[=](){return x;} Capture all variables used in the body of the lambda. x needs to be defined prior to the expression. x does not need to be in scope since it will copy the value.

[&](){return x;} Capture all references used in body of lambda. Careful with the value since x is a reference. Out of scope x by the time the lambda expression is ran would cause undefined behavior.

Extra Notes

Encourage to pick variable names that makes sense for a return type lambda expression.

Lambda expressions gets run in 2 parts.
- Evaluating, when the lambda expression is defined.
- Evaluating does the capture first.
- Invoking, when the lambda expression is when called.

C++14 allows for generic lambdas. Can use parameters with the auto data type.

Return type:

  • C++11 could have return a different type compared to the defined return type as long as it can be deduced.
  • C++14 multiple return statements, but must return same data type.
  • -> return_type Need the arrow to define return type.

Daily C++ Dose: Functional Functions

Goal

Trying to digest new C++ information everyday (personal goal). These posts aren't meant to rewrite the information read, but just for personal note taking and info digestion.

Actually got the idea from the same place I'm reading the new info. Fluent{C++}

Source

https://www.fluentcpp.com/2016/11/22/make-your-functions-functional/

Notes

  • Try to avoid global variables. They can be accessed anywhere and can break functions.
  • Be expressive with your functions. Define a clear input and output.
  • Having global variables are hidden away from the function declaration.
  • Global constants are fine to use, because they are not necessarily an I/O. They don't change through out the runtime.
  • Generally pass reference to const as a parameter. Some types, inputs can come in as a value. (const T&)
  • If you need to modify the I/O parameters, pass in a reference to not const (T&).
  • Try to return the output through the return type.
Output f(const Input& input);
// looks more natural than
void f(const Input& input, Output& output);

Of course there are reasons why you wouldn't take this approach:

  1. Performance
  2. Error Handling
  3. Multiple Return types

Performance

  • In C++, when returning a STL obj, it will move the data to a new STL obj, not copy. This prevents excessive memory usage.
  • Don't have to master Return Value Optimization, just do it and run a profiler after to check performance. Most of the time it's a non-issue.

Error Handling

  • What if there is an error and you don't want that function to crash the entire application? Perfect use case since you don't have to return anything.
  • Alternative, return boolean if successful or not. That way you can modify the output if there are no errors with the inputs and in the case of handling the error, you can return False without modifying the output object.
  • Best practice is to throw an exception when it fails.
    • For this to work, surrounding code has to be exception safe.
  • Another best practice. use the boost::optional<Output> return type. (natively available with C++17)
    • Don't forget to use auto output = f(input); where auto is the import additive. (version => C++14)

Multiple Return Types

Best practice, use structs. Clear and concise.

struct MultipleOutputs
{
    Output1 output1;
    Output2 output2;
};

//Then we can use this as a return type

MultipleOutputs f(const Input& input);

There are other options outlined in the article, but I would like to stay current with the most recent versions of C++ if possible.

Another option for C++17 and greater:

auto [output1, output2] = f(const(Input& input);
// where f returns a tuple: std::tuple<Output1, Output2> f(const Input& input);

Summary

Try to be clear and concise by returning outputs through the return type.

When that doesn't work with your use case, then just be aware of the issue of using the other methods.

Purpose

Just thought I would blog projects I'm working on.
Anything really, mostly new things that I'm learning.