0%

雖然有用過libuv,不過Boost asio可能會成為下一代C++的標準配備,還是先熟悉一下。
Boost除了Network之外,還有其他很多功能,不過此次就先以Network作介紹。
它同時支援Sync / Async兩種方式,以下是個範例。

Server

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
#include <iostream>
#include <boost/asio.hpp>

using namespace boost::asio;
using namespace boost::system;
using namespace std;
int main(int argc, char* argv[])
{
io_service iosev;
ip::tcp::acceptor acceptor(iosev,
ip::tcp::endpoint(ip::tcp::v4(), 10000));
char buf[100];
for(;;)
{
ip::tcp::socket socket(iosev);
acceptor.accept(socket);
cout << socket.remote_endpoint().address() << endl;
error_code ec;
size_t len = socket.read_some(buffer(buf, 100), ec);
if (ec)
{
cout << system_error(ec).what() << endl;
break;
}
if (memcmp(buf, "Hello server", len) == 0) {
socket.write_some(buffer("Hello client"), ec);
if (ec) {
cout << system_error(ec).what() << endl;
break;
}
}
}
return 0;
}
Read more »

這兩個特色很少被提及,寫起來,免得忘了。

final

如何防止一個類別被繼承(雖然我想不到為什麼要這麼作),以前可以用虛擬繼承來防止。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
template <typename T>
class Base {
friend typename T;
private:
Base() {}
};
class Derived : public virtual Base <Derived> {
public:
Derived() :Base() {}
};
class MoreDerived : public Derived {
public:
MoreDerived() :Derived() {}
};

其中的MoreDerived就被編譯器禁止生成。不過這樣寫實在很麻煩,於是在C++11之後友更簡單的方式。

1
2
3
4
5
6
7
8
class Base final {
public:
Base() {}
};
class Derived : public Base {
public:
Derived() {}
};

這邊只要加上final關鍵自救能達到一樣的效果。等同於C#的sealed
同樣的,C++11也從C#跟Java借鑒了不少特色

防止虛擬函數函數被override

1
2
3
4
5
6
7
8
9
10
class Base {
public:
Base() {}
virtual void show() final {}
};
class Derived : public Base {
public:
Derived() {}
void show() {} // Compile error
};

override

override顧名思義就是檢查BaseClass之中,有沒有同樣Signature的函式宣告,如果沒有的話,就會爆錯。免得產生新的函數而不自知。

1
2
3
4
5
6
7
8
9
class Base {
public:
virtual void show(int) {}
};
class Derived : public Base {
public:
void show(int) override {}
void show(double) override {} // Compile error
};

在上面的例子中,可以看到Base沒有show(double)這個Signature的宣告,因此Compiler會報錯。
因此我們可以透過 pure virtual function在編譯時檢查每個subclass都有實做此函數,而用override來檢查這函數是在VTable中。

這是最近流行的最佳化技術之一。打破所謂的Source file隔離原則,可以在Link time時對程式作最佳化。

Without LTO

直接給個範例

demo.c
1
2
3
4
5
6
7
#include <stdio.h>
int add(int, int);
int main()
{
printf("%d\n", add(1, 1));
return 0;
}
add.c
1
2
3
4
int add(int a, int b)
{
return a + b;
}
Read more »

之前遇到一個使用情況,在unique_ptr已經存在於STL Container當中,想要把他從Container移除且回傳出被分離的unique_ptr,該怎麼做。

Original idea

原來的想法很簡單,沒考慮太多

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
#include <memory>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
template<typename Func>
unique_ptr<int> RemoveFromContainer(vector<unique_ptr<int>> &c, Func pred) {
auto it = find_if(begin(c), end(c), pred);
unique_ptr<int> ret = move(*it);
c.erase(it);
return ret;
}
int main()
{
vector<unique_ptr<int>> vec;
vec.push_back(make_unique<int>(1));
vec.push_back(make_unique<int>(2));
vec.push_back(make_unique<int>(3));
unique_ptr<int> ret = RemoveFromContainer(vec, [](const unique_ptr<int> &elem) {
return *elem == 2;
});
if (ret) cout << *ret << endl;
for (auto &elem : vec)
cout << *elem << endl;
}

注意要將unique_ptr從Container移出來需要用move,然後才能將其刪除。

Read more »

Trait有很多種形式,Scala也有Trait。不過這邊講的是Rust的Trait。Scala的Trait觀念不太一樣。
一開始是看到這篇How are Rust Traits different from Go Interfaces?,後來發現跟C++觀念有點相近,於是一起比較。

Rust trait

trait長得很像Interface,不過方式不太一樣,會在編譯時期檢查資料型態。

1
2
3
4
5
6
7
8
9
10
11
trait Foo { fn bar(&self); }

impl Foo for int { fn bar(&self) {} }
impl Foo for String { fn bar(&self) {} }

fn call_bar<T: Foo>(value: T) { value.bar() }

fn main() {
call_bar(1i);
call_bar("foo".to_string());
}
Read more »

安裝systemtap本身沒什麼,不過如何找到Debug info就沒這麼簡單了。

1
$ sudo apt-get install systemtap

以下是網路大神提供的懶人法

1
2
3
$ wget http://www.domaigne.com/download/tools/get-dbgsym
$ chmod a+x get-dbgsym
$ ./get-dbgsym

曾經試著自己編譯過,不過kernel mismatch,有時間再試吧。

雖然這題目很簡單,不過看到How to zero a buffer我嚇到了,原來我之前的觀念不一定正確。
假設我們在程式中有些敏感資料(金鑰/密碼等),希望能夠在使用之後清除掉。通常我們會這麼作。

1
2
3
4
5
6
7
8
9
void dosomethingsensitive(void)
{
uint8_t key[32];

...

/* Zero sensitive information. */
memset(key, 0, sizeof(key));
}

不過這段程式碼經過最佳化之後,最後的memset被省略不做。

Read more »

最近碰到一個困難的問題,好不容易找到解答,因此紀錄起來。
當一個Socket讀寫速度不對稱的時候,該怎麼處理。
假設Socket讀取的資料室100M/S,而寫入的速度是10M/S。
如果是Blocking I/O,OS會自動幫你處理這種狀況,難怪上網找範例程式碼都沒特別處理。
由於libuv/libevent等都是Non-blocking I/O framework,,因此無法得到有用的資訊。
翻到Linux多線程服務端編程:使用muduo C++網絡庫裡面有個地方觸動了我的靈感。利用Watermark來管理讀寫動作。
後來找到Libevent的BuffereventsPython asys IO都有類似Watermark的觀念,我想這應該是可行解。

以讀取比寫入快為例

  • 當寫入的資料量大於Hih water mark時,暫停讀取動作
  • 當剩餘的寫入資料量小於Low water mark時,重新開始讀取
    反之亦然。
    Read more »

Aspect oriented programming的技術基礎可以看這裡
我想這篇已經寫得很清楚了,有興趣的話繼續Google。
接著試著用C++來實做一些Complie time的AOP方式。

Decorator Pattern

可以試著在關新的函數前面,加上Before跟After。

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
37
template <typename WrappedType>
class BaseAspect
{
protected:
WrappedType* m_wrappedPtr;

struct AfterWrapper
{
BaseAspect* m_derived;
AfterWrapper(BaseAspect* derived) : m_derived(derived) {};
void operator()(WrappedType* p)
{
m_derived->After(p);
}
};
public:
explicit BaseAspect(WrappedType* p) : m_wrappedPtr(p) {};

virtual void Before(WrappedType* p) {
// Default does nothing
};

virtual void After(WrappedType* p) {
// Default does nothing
}

std::shared_ptr<WrappedType> operator->()
{
Before(m_wrappedPtr);
return std::shared_ptr<WrappedType>(m_wrappedPtr, AfterWrapper(this));
}
};
template <template <typename> class Aspect, typename WrappedType>
Aspect<WrappedType> MakeAspect(WrappedType* p)
{
return Aspect<WrappedType>(p);
}
Read more »

這題目雖然不怎麼困難,不過解法還蠻多的,雖然原理相同,不過根據編譯氣得能力,得到的結果也不同。
題目要求: 把讀到的內容當作一個string傳回來。
不考慮OS Level API(open/CreateFile)的方式,直接用C/C++的API來做。

fopen版

大家都很熟了,用fopen/fseek/fread/fclose來達成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <cstdio>
using namespace std;
string get_file_content(const char *filename)
{
FILE *fp = fopen(filename, "rb");
if (fp)
{
string content;
fseek(fp, 0, SEEK_END);
content.resize(ftell(fp));
rewind(fp);
fread(&content[0], 1, content.size(), fp);
fclose(fp);
return content;
}
else return string();
}
Read more »