0%

根據這篇這篇,C++17的最終定案即將出爐。之前喊得狒狒洋洋的moduleconceptcoroutine沒有一個列為標準配備。相對之前喊得狒狒洋洋的大變動。還真是雷聲大雨點小。
不過還是有些實用的技術可以馬上用到,學習曲線沒那麼高..
不過這年頭每個程式語言都在新增新特性,像C語言這種實在難能可貴。不過C太低階了,開發大程式實在很麻煩。真是兩難

Structured bindings

之前說過,C++沒有return multiple value的機制,要做出類似的小果只能這麼做。

1
2
3
4
std::tuple<bool, int> getValue();
bool retB;
int retInt;
std::tie(retB, retInt) = getValue();

可以用,不過不太美觀。到了C++17有了更直觀的方法

1
auto [retB, retInt] = getValue();

這樣就很像go的語法了。更多的討論可以看Returning multiple values from functions in C++

If statement with initializer

這也是另一個我覺得很棒的點
直接看範例,為進化前

1
2
3
4
5
6
7
8
9
status_code foo() { 
{
status_code c = bar();
if (c != SUCCESS) {
return c;
}
}
// ...
}

進化後,可能的情況

1
2
3
4
5
6
status_code foo() {
if (status_code c = bar(); c != SUCCESS) {
return c;
}
// ...
}

希望這兩點未來C語言也會加回去啊

C/C++寫久了,為了轉換心情,開發了一個玩具Website Parser。把太過複雜的網頁元素清掉,只留下Title跟必要的資訊。
採用的是Python3跟BeautifulSoup4,雖然Python的文章看了不少。不過這倒是第一次認真寫點東西(雖然也只是個玩具)

獲取網頁資訊

用最簡單的方式來獲取網頁,方法不是最好,但是可以用。

1
2
3
4
def getHtml(url):
htmlDoc = request.urlopen(url).read()
htmlDoc = htmlDoc.decode('UTF-8')
return htmlDoc

將html轉成 Beautiful Soup Object

1
soup = BeautifulSoup(htmlDoc, "lxml")

砍掉無謂的網頁元件

砍掉所有符合條件的 Tag

1
2
for script in soup.find_all('script'):
script.decompose()

砍掉特定的 Tag

1
2
3
div = soup.find("div", {"id": "full-btm"})
if div is not None:
div.decompose()

砍掉 Tag中的某個屬性

1
2
3
div = soup.find("div", {"id": "full-btm"})
if div is not None:
del div["attribute"]

把清除過的 Soup 存回 HTML

1
2
3
4
html = soup.prettify("utf-8")

with open("output.html", "wb") as file:
file.write(html)

雖然只是個玩具,不過至少對Python有多一點感覺了,寫完之後才發現Sanitize HTML with Beautiful Soup這篇早就有了,真是後知後覺好幾年。

好久沒寫些東西了,雖然這篇沒什麼實用價值。在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;
}