0%

好久沒寫些東西了,雖然這篇沒什麼實用價值。在C++這種語言要模擬FP的Closure是做得到,不過不好用
這邊的Clouse跟C++的Lambda Closure不太相同。
先看正常版的C++ Code

1
2
3
4
5
6
struct Counter {
int value = 0;
void inc() { value++; }
};
Counter c;
c.inc();

一目了然的程式,也不用解釋太多。
模擬版的FP Closure

1
2
3
4
5
6
7
8
9
10
11
12
struct Counter {
int value = 0;
};
std::function<void(void)> make_inc()
{
std::shared_ptr<Counter> obj = make_shared<Counter>();
return [obj]() {
obj->value++;
};
}
std::function<void(void)> inc = make_inc();
inc();

真是囉唆。 Javascript還可以輸出多個函數。如果要用C++也能作到,像這樣

1
2
3
4
5
6
7
8
9
10
11
12
typedef std::function<void(void)> Func;
std::tuple<Func, Func> make_counter()
{
std::shared_ptr<Counter> obj = make_shared<Counter>();
Func inc = [obj]() { obj->value++; };
Func dec = [obj]() { obj->value--; };
return make_tuple(inc, dec);
}
auto funcs = make_counter();
Func inc = std::get<0>(funcs);
Func dec = std::get<1>(funcs);
inc();

tuple和get來抓取函數時再不怎麼漂亮,也許C++17的Structured bindings可以改善這個問題。

Reference

Returning multiple values from functions in C++
Emulating C++17 Structured Bindings in C++14

Stackless coroutine

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
#include <boost/asio/yield.hpp>

int foo(boost::asio::coroutine& ct)
{
std::cout << "before reenter" << std::endl;

reenter(ct)
{
std::cout << "before yield1" << std::endl;
yield std::cout << "yield1" << std::endl;
std::cout << "before yield2" << std::endl;
yield return 1;
}

std::cout << "after reenter" << std::endl;
return 2;
}

int main(int argc, char* argv[])
{
boost::asio::coroutine ct;
while (!ct.is_complete())
{
int ret = foo(ct);
std::cout << "return:" << ret << std::endl;
}
return 0;
}

有些時候,我們不想要有libstdc++.so的相依性,而只要依賴libc,如果要整個重寫原先的C++ Code花費太大。

1
2
3
4
5
6
#include <iostream>
int main()
{
std::cout << "Hello" << std::endl;
return 0;
}

看一下相依性

1
2
3
4
5
6
7
8
$ g++ test.cpp -o test
$ ldd ./test
linux-vdso.so.1 => (0x00007fff184de000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f9cfb9b9000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9cfb5f4000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f9cfb2ee000)
/lib64/ld-linux-x86-64.so.2 (0x00007f9cfbcbd000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f9cfb0d8000)

但是如果我們這樣坐的話,一切就會不同

1
2
3
4
5
6
7
8
$ g++ test.cpp -o test  -static-libstdc++ -static-libgcc
$ ldd ./test
linux-vdso.so.1 => (0x00007ffee379d000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f72ef99e000)
/lib64/ld-linux-x86-64.so.2 (0x00007f72efd63000)
$ g++ test.cpp -o test -static
$ ldd ./test
not a dynamic executable

這邊有三個選項
-static-libstdc++ 連結libstdc++.a
-static-libgcc 連結libgcc.a,如果只有-static-libgcc而沒有-static-libstdc++不起作用,但是反過來不是那麼一回事。
– ‘-static’ 去存所有相依性

Reference

How to Statically Link C and C++ Programs on Linux with gcc

以下的程式碼哪裡有問題

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
pthread_mutex_t mutex;
pthread_cond_t cond;
std::queue<int> q;
int Get()
{
pthread_mutex_lock(&mutex);
if (q.empty())
pthread_cond_wait(&cond, &mutex);
int item = q.front();
pthread_mutex_unlock(&mutex);
return item;
}
void Add(int item)
{
pthread_mutex_lock(&mutex);
q.push(item);
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond);
}

Spurious wakeup

所謂的Spurious wakeup就是被Blocking的Thread認為自己滿足條件而被喚醒,而事實上不是這麼一回事。例如

Spurious wakeups may sound strange, but on some multiprocessor systems, making condition wakeup completely predictable might substantially slow all condition variable operations.

由於有Spurious wakeup的發生,為了程式的正確性,需要為此作處理。

Consumer’s Problem

看上面的

1
2
if (q.empty())
pthread_cond_wait(&cond, &mutex);

當Spurious wakeup發生時,這個條件不一定滿足,因此要把if改成while

Producer’s Problem

由於condition variable的signal跟mutex的unlock是獨立事件,因此兩個的順序不重要,正確性都得以保證。不過這樣子的寫法一樣會造成Spurious wakeu的問題

pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond);

分析如下

  1. Thread A starts waiting for items to be added to a threadsafe queue.
  2. Thread B inserts an item on the queue. After unlocking the queue, but before it issues the signal, a context switch occurs.
  3. Thread C inserts an item on the queue, and issues the cvar signal.
  4. Thread A wakes up, and processes both items. It then goes back to waiting on the queue.
  5. Thread B resumes, and signals the cvar.
  6. Thread A wakes up, then immediately goes back to sleep, because the queue is empty.

Reference

用条件变量实现事件等待器的正确与错误做法
– [signal and unlock order

真是後知後覺啊,最近才常是用這種方式。
先來用傳統的C語言分配資源寫法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
typedef struct B1 {
...
} B1;
typedef struct B2 {
...
} B2;
typedef struct D {
B1 *p1;
B2 *p2;
} D;
D* alloc_D()
{
D *obj = (D *)malloc(sizeof(D));
if (obj == NULL)
return NULL;
obj->p1 = (B1 *)malloc(sizeof(B1));
if (obj->p1 == NULL) {
free(obj);
return NULL;
}
obj->p2 = (B2 *)malloc(sizeof(B2));
if (obj->p2 == NULL) {
free(obj->p1);
free(obj);
return NULL;
}
return obj;
}
void free_D(D *obj)
{
if (obj) {
free(obj->p2);
free(obj->p1);
free(obj);
}
}

雖說用goto可以簡化alloc的情況,不過要不要用goto見仁見智。
如果是C++的話可以這樣寫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct D {
B1 *p1;
B2 *p2;
D() {
try {
p1 = new B1;
p2 = new B2;
}
catch (...) {
delete p2;
delete p1;
}
}
~D()
{
delete p2;
delete p1;
}
};

在Constructor中可以接收Exception,保證Object要嘛無法使用,要嘛是一個完整的狀態。
這邊看到刪除的部份程式碼重複了,因此用Smart pointer可以更進一步。

1
2
3
4
5
6
struct D {
unique_ptr<B1> p1;
unique_ptr<B2> p2;
D() : p1(new B1), p2(new B2) {}
~D() = default;
};

Reference

Is it ever not safe to throw an exception in a constructor?
C++ : handle resources if constructors may throw exceptions

有時間回頭看Java,發現以前沒注意到的Checked Exception,看了一下主流語言只有Java和Python有做。先不論使用與否正反意見,討論一下他在語言之中的定位。

Simple Example

看一下有Checked Exception的版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class CheckExceptionTest {
public void storeDataFromUrl(String url) {
try {
String data = readDataFromUrl(url);
} catch (BadUrlException e) {
e.printStackTrace();
}
}
public String readDataFromUrl(String url)
throws BadUrlException {
throw new BadUrlException("Bad URL: " + url);
}
};
class BadUrlException extends Exception {
public BadUrlException(String s) {
super(s);
}
};

和沒有的版本

1
2
3
4
5
6
7
8
9
10
11
12
13
class UncheckExceptionTest {
public void storeDataFromUrl(String url) {
String data = readDataFromUrl(url);
}
public String readDataFromUrl(String url) {
throw new BadUrlException("Bad URL: " + url);
}
};
class BadUrlException extends RuntimeException {
public BadUrlException(String s) {
super(s);
}
};

差異在於兩個地方
– Function Signature有加上throws BadUrlException
– Exception繼承的Base Class不同
這樣可以提醒編譯砌牆志檢查是否有無處理的情況(是否妥善處理就是另外一件事了),立意良善,不過

How to deal with Exception

以上麵那個利子來說,如果認為storeDataFromUrl不是一個處理Exception的好地方,只好修改這個Function的Signature

1
2
3
public void storeDataFromUrl(String url) throws BadUrlException {
String data = readDataFromUrl(url);
}

throws會蔓延的,如果A->B->C->D->E的呼叫方式。A處理異常,E有Checked Exception,那麼B,C,D,E都要修改Function Signature。

Interface issue

在throws Propagate過程中,如果遇到Interface的Issue情況更加惡化

1
2
3
4
5
interface InterfaceCheck {
public void storeDataFromUrl(String url);
};
class CheckExceptionTest implements InterfaceCheck
};

由於我們修改了storeDataFromUrl的Signature,所以Interface部份要跟著改,而所有實做此Interface的Signature都要改。
就跟滾雪球一樣,越滾越大。

Possible Solution

如果同樣的我們想再A處理Exception,必須在原處做些簡單的處理。

1
2
3
4
5
6
7
8
9
public void storeDataFromUrl(String url) {
try {
String data = readDataFromUrl(url);
} catch (BadUrlException e) {
// Do something about e
// Rethrow a runtime exception
throw new RuntimeException("BadUrl");
}
}

這麼麻煩,那還不直接用Unchecked Exception就好了,所以這一點爭議不少。

Reference

Why doesn’t C# have checked exceptions?
Does Java need Checked Exceptions?
The Trouble with Checked Exceptions
Checked or Unchecked Exceptions?
Checked vs. Unchecked Exceptions: The Debate Is Not Over
Checked Exceptions are Evil

AFL-fuzz

官網

1
2
3
4
5
$ wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz
$ tar zxvf afl-latest.tgz
$ cd afl-2.05b
$ make
$ sudo make install
1
2
3
4
5
$ wget https://ftp.gnu.org/gnu/binutils/binutils-2.25.tar.gz
$ tar zxvf binutils-2.25.tar.gz
$ cd binutils-2.25
$ CC=afl-gcc ./configure
$ make

Test

1
2
3
$ mkdir afl_in afl_out
$ cp /bin/ps afl_in
$ afl-fuzz -i afl_in -o afl_out ./binutils/readelf -a @@

Stackoverflow 看到的,最近剛好再看Effective Modern C++,覺得有用,紀錄起來。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <type_traits>
#include <typeinfo>
#ifndef _MSC_VER
# include <cxxabi.h>
#endif
#include <memory>
#include <string>
#include <cstdlib>

template <class T>
std::string
type_name()
{
typedef typename std::remove_reference<T>::type TR;
std::unique_ptr<char, void(*)(void*)> own
(
#ifndef _MSC_VER
abi::__cxa_demangle(typeid(TR).name(), nullptr,
nullptr, nullptr),
#else
nullptr,
#endif
std::free
);
std::string r = own != nullptr ? own.get() : typeid(TR).name();
if (std::is_const<TR>::value)
r += " const";
if (std::is_volatile<TR>::value)
r += " volatile";
if (std::is_lvalue_reference<T>::value)
r += "&";
else if (std::is_rvalue_reference<T>::value)
r += "&&";
return r;
}

最近想從IP Camera和FFMpeg搭上陷,不過IP Cam走得不是標準的v4l2這套,只好另尋他路。
看了幾篇文章,時做了之後可以用,增添了自信心。
紀錄一下整個思路。

Step 1

仙分配一塊記憶體,當作IO Context的Buffer使用

1
2
const int iBufSize = 32 * 1024;
BYTE* pBuffer = new BYTE[iBufSize];

Step 2

向FFMpeg要求建立AVIOContext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int ReadFunc(void* ptr, uint8_t* buf, int buf_size)
{
// Left to implement
}
int64_t SeekFunc(void* ptr, int64_t pos, int whence)
{
// Left to implement
}
AVIOContext* pIOCtx = avio_alloc_context(pBuffer, iBufSize, // internal Buffer and its size
0, // bWriteable (1=true,0=false)
pContext, // user data ; will be passed to our callback functions
ReadFunc,
0, // Write callback function (not used in this example)
SeekFunc);

上面的ReadFuncSeekFunc就代表Read和Seek的動作,而pContext就是有關的上下文,ReadFuncSeekFunc被呼叫時,pContext會被傳入ptr中。因此需要自行轉型成正確的上下文。

Step 3

建立AVFormatContex,這邊我是採用比較偷懶的作法,我已經知道InputFormat了,所以不需要Probe。如果要Probe可以參考Reference的連結。

1
2
3
4
AVFormatContext* pCtx = avformat_alloc_context();
pCtx->pb = pIOCtx;
pCtx->iformat = av_find_input_format("h264");
pCtx->flags = AVFMT_FLAG_CUSTOM_IO;

Step 4

依照FFMpeg正常的流程使用

1
2
if (avformat_open_input(&pCtx, "", 0, 0)) != 0)
// Error Handling

接著就跟一般無異了

Final

使用完後需要釋放資源

1
2
3
avformat_close_input(pCtx);  // AVFormatContext is released by avformat_close_input
av_free(pIOCtx); // AVIOContext is released by av_free
delete[] pBuffer;

Reference

又一個實驗性質的玩意,由於目前沒有IDE支持,一切從零開始。

一個簡單的範例

1
2
3
4
5
module Math;
export int add(int x, int y)
{
return x + y;
}

命名成add.ixx
接著main進來了

1
2
3
4
5
6
7
#include <stdio.h>
import Math;
int main()
{
printf("2 + 3 = %d\n", add(2, 3));
return 0;
}

以下是編譯過程

1
2
3
4
C:\> cl /c /experimental:module add.ixx
C:\> cl /experimental:module /module:reference Math.ifc main.cpp add.obj
C:\> main.exe
2 + 3 = 5

一切都看起來很美好
如果我們新增一個sub.ixx

1
2
3
4
5
module Math;
export int sub(int x, int y)
{
return x - y;
}

main也順勢改寫

1
2
3
4
5
6
int main()
{
printf("2 + 3 = %d\n", add(2, 3));
printf("3 - 2 = %d\n", sub(3, 2));
return 0;
}

這下子編譯救出錯了

1
2
3
4
5
C:\> cl /c /experimental:module add.ixx
C:\> cl /c /experimental:module sub.ixx
C:\> cl /experimental:module /module:reference Math.ifc main.cpp add.obj sub.obj
main.cpp
main.cpp(5): error C3861: 'add'

這個情況看起來Math.ifc被sub.ixx複寫了,以至於add的資訊消失。雖然可以用Module subdomain斃掉,不過這樣子很難用啊。

Template

試著在ixx當中放一些Template function,不過完全無效。還是只能放在Header file當中。

結論

有了Module之後,除了Template這種避不開的之外,Header file的重要性可能會大大降低 (再也不用擔心Winsock.h 跟 Windows.h的恩怨情仇了)。也能加速奇編譯速度。不過還在實驗階段,靜觀其變吧。