Category Archives: C++

Using std::future as a coroutine in C++20

In C++20 it is possible to do this:

std::future<int> compute(as_coroutine) {
  int a = co_await std::async([] { return 6; });
  int b = co_await std::async([] { return 7; });
  co_return a * b;
}
 
std::future<void> fail(as_coroutine) {
  throw std::runtime_error("bleah");
  co_return;
}
 
int main() {
  std::cout << compute({}).get() << '\n';
 
  try {
    fail({}).get();
  } catch (const std::runtime_error &e) {
    std::cout << "error: " << e.what() << '\n';
  }
}
(more…)

Waitable QTimer with C++20 coroutines

Found an example of overriding co_await operator and was able to implement my own version:

#include <QTimer>
#include <QMetaObject>

#include <chrono>
#include <coroutine>

//QTimer accepts only milliseconds.
class TimeAwaitable
{
public:

    explicit TimeAwaitable(std::chrono::milliseconds d) : m_d(d) {}

    ~TimeAwaitable()
    {
        if (m_connection)
        {
            QObject::disconnect(m_connection);
        }
    }

    bool await_ready() const noexcept
    {
        return m_d <= std::chrono::milliseconds(0);
    }

    void await_suspend(std::coroutine_handle<> h) noexcept
    {
        m_connection = QObject::connect(&m_timer, &QTimer::timeout,
            [this, h]()
            {
                QObject::disconnect(m_connection);

                h.resume();
            });

        m_timer.setSingleShot(true);
        m_timer.start(m_d);
    }

    void await_resume() const noexcept {}

private:

    std::chrono::milliseconds m_d;
        
    QTimer m_timer;

    QMetaObject::Connection m_connection;
};

inline TimeAwaitable operator co_await(std::chrono::milliseconds d) noexcept
{
    return TimeAwaitable{ d };
}

For those who did not work with QT, QTimer::singleShot simply calls the lambda on the current thread after a given interval.

(more…)

Did you already learn what are C++ concepts?

In C++20 we can do this:

#include <ranges>
#include <vector>

template <std::ranges::range Range>
    requires std::same_as<std::ranges::range_value_t<Range>, int>
auto TransformIt(Range r)
{
    return r | std::views::transform([](int n) { return n * n; });
}

int main()
{
    std::vector<int> v;
    auto r = TransformIt(v);
    return 0;
}
(more…)

std::unique_ptr with a deleter that throws an exception

The program below is terminated when the deleter throws the exception:

#include <iostream>
#include <memory>

struct X
{
    int value;
};

struct Deleter
{
    void operator()(X* p) noexcept(false)
    {
        if (p->value == 0)
        {
            throw std::logic_error("Can't delete a zero.");
        }

        delete p;
    }
};
(more…)

An example of heterogeneous lookup with std::unordered_set in C++20

std::unordered_set has a template find function in C++20. To make it work I define a custom hash and a custom equality compares as follows:

struct BotSettings
{
    std::string type;
    std::string name;
    bool started;
};
(more…)

Array subscript -N is outside array bounds warning in GCC11

Consider the code below with UB:

template <class Derived>
class A
{
public:

    Derived * get() { return static_cast<Derived *>(this);}

private:

    int m_a;
};

class B
{
public:

    double m_b;
};

class X : public B, public A<X>
{
public:

    int m_x;
};

int main()
{
    A<X> a;
    std::cout << a.get();
    return 0;
}
(more…)

Compiling Awl on Ubuntu 18 and 20 with GCC 11

Below I provided the instruction on how to build Awl on Ubuntu 18 and 20.

On Ubuntu 18 you do an extra step to install CMake version >= 3.12:

wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | sudo apt-key add -
sudo apt-add-repository 'deb https://apt.kitware.com/ubuntu/ bionic main'
sudo apt update

Then do the following steps on both Ubuntu 18 and 20:

sudo apt install cmake
cmake --version
(more…)

C++ tasks from Yandex

Task 1

At an interview I was asked how to by a given vector of integers build resulting vector containing the products of all the elements except current. Below I provided my solution in C++:

#include <vector>
#include <iostream>

using V = std::vector<int>;

V func(const V& v)
{
    V result;
    result.resize(v.size());

    int product = 1;

    for (size_t pos = 0; pos != v.size(); ++pos)
    {
        result[pos] = product;

        int a = v[pos];
        product *= a;
    }
(more…)

Range-based for loop with universal reference.

It is possible to iterate over std::vector with &&:

#include <vector>

class A {};

int main()
{
    std::vector<A> vec;
    for (auto&& v : vec)
    {
        static_cast<void>(v);
    }

    return 0;
}

Links:

An annoying question about auto in C++

I was recently asked during a C++ job interview what are the types of riN variables in the code below:

int val = 25;

int foo() { return val; }
int& foo1() { return val; }
//warning: type qualifiers ignored on function return type
/*const*/ int foo2() { return val; }
const int& foo3() { return val; }

int main()
{
  auto ri = foo(); 
  auto ri1 = foo1();
  auto ri2 = foo2();
  auto ri3 = foo3();

  //cannot bind non-const lvalue reference of type 'int&' to an rvalue of type 'int'
  //auto& ri4 = foo();
  auto& ri5 = foo1();
  //cannot bind non-const lvalue reference of type 'int&' to an rvalue of type 'int'
  //auto& ri6 = foo2();
  auto& ri7 = foo3();

  auto&& ri8 = foo();
  auto&& ri9 = foo1();
  auto&& ri10 = foo2();
  auto&& ri11 = foo3();
    
  return 0;
}

auto ignores the type qualifiers and references, so looks like the types are simply int, int& and int&&.