Category Archives: C++

PicoBf: A brainf**k lang REPL environment for Pico Pi

こんぺこ〜こんぺこ〜こんぺこ!

I heard that Pico Pi @Raspberry_Pi only has MicroPython REPL at the moment, so I had some fun writing a brainf**k REPL in C++17 for Pico Pi XDDDDD (btw, the output image is also written in bf lang!)

The brainf**k interpreter is adapted from a previous project, you can find it in this post, Brainfuck Interpreter in C++17——A Modern Approach to Kill Your Brain.

Source code on GitHub, https://github.com/BlueCocoa/pico-bf

Hardware UART communication between Raspberry Pi 4 and Arduino Micro

作りましょう 作りましょう

あなたと私の世界をさぁ作りましょう

始めましょう 始めましょう

なにから始めましょう(ん~!?)

OK, let's start from communicating between Raspberry Pi 4 and Arduino Micro with hardware UART. For Raspberry Pi, GPIO pin 14 and 15 are the TX and RX pin of UART correspondingly.

1. Physical Connection

The first thing to note is that all GPIO pins on Raspberry Pi are 3.3v tolerance and Arduino Micro runs at 5v. If we connect Raspberry Pi with Arduino Micro directly, chances are that your Raspberry Pi can be damaged. Thus we need a bi-directional logic level converter.

But unfortunately, the one I brought came with separate headers that I needed to solder them onto the converter board by myself.

Well, the solder work may look just so so, but it works. That's what matters most. XD

The image below is my Arduino Micro and we can see that from right to left on the top bank, the third and fourth pin are the TX and RX pin.

Next, we need to connect them as the image demonstrates below.

Now we have done the physical part, time to move on to the software part.

Continue reading Hardware UART communication between Raspberry Pi 4 and Arduino Micro

Maybe you can implement Maybe in this way in C++

I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years. —— Tony Hoare, QCon London 2009

This post demonstrates a naive idea of how to implement Maybe trait (like std::option::Option in Rust or Maybe in Haskell) in C++17 and above.

What drives us to use Maybe is to avoid ambitious result. For example, if you're going to write a simple function that handle division.

int div_v1(int a, int b) {
    return a / b;
}

Of course we know that if b is 0, a floating point exception will be raised and your program will crash.

Well, technically we can just let the program crash, but most of the time we want to handle this illegal input more elegantly. So a if statement should be put in this function to see whether b is 0.

int div_v2(int a, int b) {
    if (b == 0) {
        fprintf(stderr, "[ERROR] %s: b is 0", __PRETTY_FUNCTION__);
        // but what should we return?
        return 0;
    } else {
        return a / b;
    }
}

However, it seems that there is no appropriate value to return if b is 0. Think about div_v2(0, 233), the result should exactly 0, so 0 cannot be used as an identification of illegal input.

Any other number? Then think about div_v2(a, 1), since variable a can be any number, and b is 1, so there is no such number we can use as identification of illegal input.

Do we have any workaround? Let's see. Try to return NULL if b is 0. But NULL is just an alias of 0 before C++11 standard.

If we use nullptr, which introduced since C++11 so that we can distinguish 0 and nullptr , the code will be

int * div_v3(int a, int b) {
    if (b == 0) {
        fprintf(stderr, "[ERROR] %s: b is 0", __PRETTY_FUNCTION__);
        return nullptr;
    } else {
        // since we cannot return a temporary variable on stack
        // we have to explicitly allocate memory
        int * res = new int;
        *res = a / b;
        return res;
    }
}

int main(int argc, char *argv[]) {
    int * res = div_v3(0, 3);
    if (res != nullptr) {
        printf("%d\n", *res);
        
        // which introduced extra memory management
        delete res;
    }
}

As you can see, this requires extra memory management. Maybe you will argue that we can do this

int * div_v4(int a, int b, int &result) {
    if (b == 0) {
        fprintf(stderr, "[ERROR] %s: b is 0", __PRETTY_FUNCTION__);
        return nullptr;
    } else {
        result = a / b;
        return &result;
    }
}

int main(int argc, char *argv[]) {
    int result;
    int * ret = div_v4(0, 3, result);
    if (ret != nullptr) {
        printf("%d\n", result);
    }
}

And if you're using C++17 standard, you can write the code of main part more compactly.

// compile with `-std=c++17`
if (int result; div_v4(0, 3, result) != nullptr) {
    printf("%d\n", result);
}

Well, this is where a "but" comes in. You cannot transform the math expression (100 / 10) / (200 / 50) in one line. So instead of writing something we could do with div_v1

int result = div_v1(div_v1(100, 10), div_v1(200, 50));

we can only write

// compile with `-std=c++17`
if (int result_a; div_v4(100, 10, result_a) != nullptr) {
    if (int result_b; div_v4(200, 50, result_b) != nullptr) {
        if (int result_c; div_v4(result_a, result_b, result_c) != nullptr) {
            // what a hell
        }
    }
}

In order to be safe and easy, we can write a maybe.hpp that wraps all functionalities of Maybe

Continue reading Maybe you can implement Maybe in this way in C++

C/C++ 中非法的 void main() 与 main() 函数返回值的作用

这篇 post 是主要是写给 Association of Robots and Artificial Intelligence 的小伙伴的~主要说说为什么 void main() 是非法的,以及 main() 函数的返回值到底有什么用~

先从 void main() 讲起吧~这里就先引用 C++ 之父,Bjarne Stroustrup,在他的博客中写过的一篇问答:Can I write "void main()"?

Can I write "void main()"?

The definition
                                      void main() { /* ... */ }
is not and never has been C++, nor has it even been C. See the ISO C++ standard 3.6.1[2] or the ISO C standard 5.1.2.2.1. A conforming implementation accepts
                                      int main() { /* ... */ }
and
                                      int main(int argc, char* argv[]) { /* ... */ }
A conforming implementation may provide more versions of main(), but they must all have return type int. The int returned by main() is a way for a program to return a value to "the system" that invokes it. On systems that doesn't provide such a facility the return value is ignored, but that doesn't make "void main()" legal C++ or legal C. Even if your compiler accepts "void main()" avoid it, or risk being considered ignorant by C and C++ programmers.

翻译:这种写法 void main() { /* ... */ },从来都没有在 C/C++ 中存在过,根据 ISO C++ 标准 3.6.1 或者 ISO C 标准 5.1.2.2.1,只有 int main() {/* ... */} 或者是 int main(int argc, char* argv[]) {/* ... */} 才是可接受的。

A conforming implementation may provide more versions of main(), but they must all have return type int. The int returned by main() is a way for a program to return a value to “the system” that invokes it. On systems that doesn’t provide such a facility the return value is ignored, but that doesn’t make “void main()” legal C++ or legal C. Even if your compiler accepts “void main()” avoid it, or risk being considered ignorant by C and C++ programmers. —— Bjarne Stroustrup

翻译:符合规范的写法可能会有一些别的版本,但是他们都必须返回 int。这个main()返回的 int是用来会返回给调用这个程序的“系统”的。在没有提供这样的功能的系统中,这个返回值会被忽略,但是那也绝不使得void main在 C/C++ 中是合法的。即便你的编译器让你编译过了,或者就是仔细考虑过这么写的后果/风险

比如,我们来编译一下如下的 C 代码

void main() {
    
}

那么编译时就会报一个警告

编译器告诉我们 main() 函数应该有的返回类型是 int 而不是 void

此外,void main() 也不在 C89 或者 ANSI C 规范中受支持,要么会报错,要么会产生警告。事实上,没有任何一个 C/C++ 标准支持这种形式的 main() 函数。以下是依次以 C89 标准和 ANSI C 标准编译时会有的输出。

Continue reading C/C++ 中非法的 void main() 与 main() 函数返回值的作用

Using C/C++ for Python Extension

In general, C/C++ can be used to extend the functionality of Python with almost the highest performance you demand. To write a Python extension in C/C++ is relatively easy.

I'll show a simplified extension which is used in real life. This extension is made to extract records in a special file format, .pcap, and .pcap file is used to store the captured network packets so that the network activities can be analysed later.

Although there are many alternatives, they cannot achieve the goal in reasonable time. One of these alternatives is scapy, please don't get me wrong, scapy is a fabulous networking package. It can automatically parse all the records in .pcap file, which is an amazing feature. However, the parsing work will also take significant amount of time, especially for a large .pcap file with hundreds of thousands records inside.

At that time, my goal was quite straightforward. The time when captured the packet, from which source IP the packet was sent, and the destination IP of the packet. Given these demanding, there is no need to parse any record as deep as scapy would do. I can just check whether it contains IP layer or not, and if yes, extract the source IP and destination IP. Otherwise I'll skip to next record. And that's all.

I decided to name the extension as streampcap. And the class name would be StreamPcap so that I can write my Python code as below.

from streampcap import StreamPcap

pcap = StreamPcap("sample.pcap")
packet = pcap.next()
while packet is not None:
    print("{} {} {}".format(packet["time"], packet["ip_src"], packet["ip_dst"]))
    packet = pcap.next()

In order to implement this functionality, python-dev should be installed if the OS is Ubuntu/Debian/CentOS and etc Linux based operating systems. As for macOS, personally I use miniconda to manage the Python environment, and I think that miniconda will automatically get the same thing done. And miniconda is also available for Linux based OS. Life is easier!

Continue reading Using C/C++ for Python Extension

Brainfuck Interpreter in C++17——A Modern Approach to Kill Your Brain

It has been a long time that I wrote my last C++ code. And obviously, C++ has released the new standard, C++17, for a few months. There are a lot features in which has been introduced or changed.

One out many is the pattern matching using std::variant, std::monostate and std::visit. This feature could lead the data-oriented design easier in modern C++. And it's not that hard to write a (possibly buggy) brainfuck interpreter with this feature.

Although with that said, we still need some extra 'hacking' to achieve a more elegant code style. There is a blog post by Marius Elvert explaining a two-line visitor in C++, with which you could write a few lambda expressions in one visitor and then the std::variant could be dispatched by the receiving type of those lambda.

auto my_visitor = visitor{
    [&](int value) { /* ... */ }
    [&](std::string const & value) { /* ... */ }
};

To accomplish the goal, Marius found that lager just uses two lines of code. The details are explained in aforementioned blog post. And for the readability of the code today, I just substituted the name of the struct.

And if you're in a hurry, the full code is on my GitHub, #/brainfuck-cpp17

template<class... Ts> struct brainfuck_vm : Ts... { using Ts::operator()...; };
template<class... Ts> brainfuck_vm(Ts...) -> brainfuck_vm<Ts...>;

And the next step is to define all the 8 valid ops of brainfuck.

Continue reading Brainfuck Interpreter in C++17——A Modern Approach to Kill Your Brain

Just for fun: Compile time fibonacci

所以继续摸个鱼,用 C++ 模版编程写个 fibonacci 计算

当然一开始只是顺手玩玩了,因为 C++ 这里的模版匹配其实挺像函数式编程的(似乎还有人曾用 C++ 的模版匹配来做过 SAT solver 的样子)

先把 C++ 编译时的放在下面,一会儿再补一个正经函数式编程的代码,然后再加一个更优雅的函数式实现(Elixir),最后用 Python 和 C++ 再模仿一下函数式的吧~

Continue reading Just for fun: Compile time fibonacci

Have some fun with C++ template programming and compile time string obfuscation

嗯,摸鱼的时候看到了一个 C++ 编译时混淆字符串的实现,urShadow/StringObfuscator. (然后还顺便又玩了一下 C++ 模版编程)

怎么说呢,这样的通过C++模版来现实的编译时混淆其实特征还是相对比较明显的,另有一种也是在编译的时候去混淆,但是则是由编译器/编译器插件来现实的。

至于说性能的话,直观来说对于绝大多数日常应用,两种方式相比不做混淆来讲,也没有可观察到的区别。不过我也没去做benchmark,有兴趣的倒是可以一试。

urShadow/StringObfuscator 使用上比较简单,但相比编译器插件的方式,还是会需要对代码做出一定的修改。

#include <iostream>
#include "str_obfuscator.hpp"

int main(int argc, const char * argv[]) {
    std::cout << cryptor::create("Hello, World!").decrypt() << std::endl;
    return 0;
}

总的来说实现上很简单,很直接,利用 C++ 模版参数取到要混淆的字符串的长度 S与其本体 str。

Continue reading Have some fun with C++ template programming and compile time string obfuscation

Dijkstra's Shortest Path First algorithm

#include <iostream>
#include <vector>
#include <stack>

using namespace std;

/*
 输入
 6
 9
 0 1 2
 0 2 3
 1 2 2
 1 3 4
 2 3 1
 2 4 3
 3 4 2
 3 5 7
 4 5 4
 */
int main(int argc, const char * argv[]) {
    // 一共有多少个顶点
    int n;
    cin >> n;
    
    // 初始化这个
    int64_t * graph = new int64_t[n * n];
    for (int row = 0; row < n; row++) {
        for (int col = 0; col < n; col++) {
            graph[row * n + col] = INT64_MAX;
        }
    }
    
    // 一共有多少组边
    int p;
    cin >> p;
    for (int i = 0; i < p; i++) {
        // 依次输入每一条边
        int start, end, distance;
        cin >> start >> end >> distance;
        // 假设我们是无向图
        // 无向图具有对称性~
        graph[start * n + end] = distance;
        graph[end * n + start] = distance;
    }
    
    // 保存定点是否被访问过
    vector<bool> visited;
    visited.resize(n);
    
    // 保存源点到各点的最短距离
    vector<int64_t> distance;
    distance.resize(n);
    // 起始点到自己的距离是0
    distance[0] = 0;
    // 其余各点都是正无穷
    for (int i = 1; i < n; i++) {
        distance[i] = INT64_MAX;
    }
    
    // 保存路径的数组
    vector<int> path;
    path.resize(n);
    
    // 从源点开始吧/
    int current = 0;
    while (true) {
        // 标记当前节点已经访问
        visited[current] = true;
        // 找出当前点可以一步访问到的所有的点
        for (int i = 0; i < n; i++) {
            if (visited[i] == false && // i 没有被访问过
                graph[current * n + i] != INT64_MAX // 并且current可以走到i这个顶点
                ) {
                //  0 ->...-> current -> i
                int64_t access_i_via_current = distance[current] + graph[current * n + i];
                //  0 ->...-> i
                int64_t access_i_via_other_path = distance[i];
                
                // 如果经过当前的点
                // 可以使得到 i 的距离变短的话
                if (access_i_via_current < access_i_via_other_path) {
                    // 那么更新路径~下面这个赋值的意思是
                    // 下标 i 这个点
                    // 应该从 current 来更近
                    path[i] = current;
                    // 更新距离
                    distance[i] = access_i_via_current;
                }
            }
        }
        
        int64_t new_minimum = INT64_MAX;
        for (int i = 0; i < n; i++) {
            // 从还没有访问过的点中
            if (visited[i] == false) {
                // 找出有着最小距离的点扩展
                if (distance[i] < new_minimum) {
                    current = i;
                    new_minimum = distance[i];
                }
            }
        }
        
        // 如果所有的点都访问过了
        // 那么这个 new_minimum 就还是初始值 +INF
        if (new_minimum == INT64_MAX) {
            // 此时就说明可以结束了
            break;
        }
    }
    
    cout << "minimum distance 0->" << n-1 << " is " << distance[n - 1] << '\n';
    
    // 保存最短路的路径
    stack<int> minimum_path;
    // 从最后一个点往回看
    current = n - 1;
    minimum_path.push(current);
    while (minimum_path.top() != 0) {
        // 从某个点到 current 是最近的
        // 把那个点放进去~
        minimum_path.push(path[current]);
        // 再来准备看是哪个点  那个点最近
        current = path[current];
    }
    
    // 按顺序输出整条路径
    while (!minimum_path.empty()) {
        cout << minimum_path.top() << ", ";
        minimum_path.pop();
    }
    cout << endl;
}