0%

前情提要

看云是個非常不錯的文件共享平台, 有提供mobi, epub, pdf不同的格式放至電子閱讀器上看, 不過人生最困難的就是那個but, 雖然可以在E-INK上看, 不過由於CSS Layout的關係, 在螢幕上看的效果跟在E-INK上相差太大
我就是不想一直盯著螢幕, 所以只好自力救濟, 寫出一個Converter

Golang practice

由於主要語言是C/C++, 之前有看過golang的語法而未正式動手
這次特別練習了一下, 不難
運用到的Library只有goquery
將網頁抓下來, 去除掉不必要的部分, 再利用pandoc將html轉成markdown格式

Github Repo

go_epub

從最簡單的情況說起

煤捕捉任何狀態的lambda可以直接轉化成普通的C function型態

1
2
3
4
5
6
7
8
typedef void(*callback)();
void testFunc(callback cb)
{
cb();
}
testFunc([] {
cout << "Lambda function" << endl;
});

帶狀態的情形

通常callback還會帶一個void *參數,可以讓你上下其手

1
2
3
4
5
6
7
8
9
10
typedef void(*callback)(void *);
void testFunc(callback cb, void *user_data)
{
cb(user_data);
}
int v = 123;
testFunc([](void *user_data) {
int *v = static_cast<int *>(user_data);
cout << "Lambda function " << *v<< endl;
}, &v);

當需要更多上下文時,需要自行定義structure

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// local variables
int x = 0;
float y = 1;

// locally defined uncopyable, unmovable type
struct MtEverest
{
MtEverest() = default;
MtEverest(const MtEverest& that) = delete; // no copy
MtEverest(const MtEverest&& that) = delete; // no move
} mt_everest;

// create "user-data" payload
auto payload = std::tie(x, y, mt_everest);
testFunc([](void *user_data) {
auto& payload_tup = *reinterpret_cast<decltype(payload)*>(user_data);
auto& xx = std::get<0>(payload_tup);
auto& yy = std::get<1>(payload_tup);
auto& me = std::get<2>(payload_tup);
}, &payload);

更高明的解法

當C API沒辦法有void *的指標讓你上下其手,或是packing/unpacking的程式碼讓人不耐
可以考慮以下這個解法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
template <typename Lambda>
static auto lambdacb(Lambda &&l)
{
thread_local auto* p = &l; // initial assignment, allows using auto
p = &l;
return []() { return (*p)(); };
}

typedef void(*callback)();
void testFunc(callback cb)
{
cb();
}
// local variables
int x = 0;
float y = 1;

testFunc(lambdacb([&x, &y] {
cout << x << " " << y << endl;
}));

Reference

Lambda Magic
Lambda Callbacks
Technical Debt

看到這篇有感而發,現實中常見的問題之一,為了方便說明,把原先的問題簡化,求鄰近元素後者比前面大的數對

最初的方法

原先我只會用這種方法

1
2
3
4
5
6
7
int calc(const std::vector<int> &v)
{
int count = 0;
for (size_t i = 0; i < v.size() - 1; i++)
if (v[i + 1] > v[i]) count++;
return count;
}

利用vector來作,實在不高明

Better Solution

參考上面blog的作法

1
2
3
4
5
6
7
8
9
template <typename T>
int calc(const T& v)
{
int count = 0;
for (auto it1 = v.cbegin(), it2 = v.cend(); it1 != v.cend(); it2 = it1, ++it1)
if (it2 != v.end())
if (*it1 > *it2) count++;
return count;
}

好一點了,不限定要是vector,不過還是要思考一下it1和it2的關聯性

Range-V3 Solution

可能成為下一代STL的Range-v3,其作法就類似FP中的pipeline的方式處理,隱藏了iterator的存在

1
2
3
4
5
6
7
template <typename T>
int calc(const T& v)
{
using namespace ranges;
auto larger = [](auto front, auto back) { return front > back; };
return distance(v | view::adjacent_remove_if(larger)) - 1;
}

Range-V3 Solution Ver 2, Sliding Window

Range-v3最近加入了Sliding Window的觀念,比起上面的方式更加通用,不過還是不知道怎麼把他轉成Ranges轉成Tuple,只好寫成這樣

1
2
3
4
5
6
7
8
9
10
11
template <typename T>
int calc(const T& v)
{
using namespace ranges;
return count_if(v | view::sliding(2), [](const auto &v) {
auto begin = ranges::begin(v);
auto front = *begin++;
auto back = *begin++;
return front < back;
});
}

Conclusion

抽象的程度越高,Debug的難度也自然越高,看著gcc或是clang吐出來的compilation error真是一個頭兩個大,尤其是跟Range-v3扯上關係

Reference

range-v3
Super expressive code by Raising Levels of Abstraction
Ranges: the STL to the Next Level

最近摸索出來的心得,先來看看傳統的 Composite design pattern要怎麼作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <vector>
#include <memory>
using namespace std;

class Component
{
public:
virtual ~Component() = default;
};

class Leaf : public Component
{
};

class Composite : public Component
{
vector<unique_ptr<Component>> children;
public:
void add(Component *ele)
{
children.push_back(std::unique_ptr<Component>(ele));
}
};

這個方法不差,不過還是有幾點可以改良的
– 每個Subtype都必須繼承Component,就算沒有任何is-a的關聯性還是必須這麼作
– 當要Clone一份物件出來的話,需要另外一個Prototype Pattern
例如

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
class Component
{
public:
virtual ~Component() = default;
virtual Component* clone() = 0;
};

class Leaf : public Component
{
public:
Component* clone() override { return new Leaf(); }
};

class Composite : public Component
{
vector<unique_ptr<Component>> children;
public:
void add(Component *ele)
{
children.push_back(std::unique_ptr<Component>(ele));
}
Component* clone() override {
Composite* composite = new Composite();
for (const auto &child : children)
composite->children.push_back(
std::unique_ptr<Component>(child->clone()));
return composite;
}
};

每個都這麼作實在很醜,並且又容易錯

Solution based on std::variant

使用std::variant可以解決上面的問題,乾淨俐落

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Leaf;
class Composite;
using CompositeVar = variant<Leaf, Composite>;
class Leaf final
{
public:
Leaf() = default;
Leaf(const Leaf&) = default;
};

class Composite final
{
vector<CompositeVar> children;
public:
Composite() = default;
Composite(const Composite&) = default;
void add(const CompositeVar &ele)
{
children.push_back(ele);
}
};

不過這也不是萬用解,當你的CompositeVar 中的type數目是有限的,可以採用這個方式
不然就是每加入一種Type,就必須重新編譯一次,反而沒有舊版的彈性

安裝 Emscripten SDK

首先必須先安裝以下package

1
$ apt-get install wget python git g++ cmake nodejs

從[網站(http://kripken.github.io/emscripten-site/docs/getting_started/downloads.html)下載Portable SDK進行編譯安裝。安裝過程如下

1
2
3
4
5
6
7
$ tar zxvf emsdk-portable.tar.gz
$ cd emsdk_portable
$ ./emsdk update
$ ./emsdk install clang-tag-e1.34.1-64bit
$ ./emsdk install emscripten-tag-1.34.1-64bit
$ ./emsdk activate
$ ./emsdk_env.sh

修改~/.emscripten`

NODE_JS = nodejs

寫個程式測試

1
2
3
4
5
#include <stdio.h>
int main( )
{
printf("Hello World!\n");
}

將奇編譯成Javascript

1
$ ./emcc hello.c -o hello.out.js

用nodejs執行它

1
$ nodejs hello.out.js

Asm.js

Asm.js是Javascript的一個子集合,限制Javascript的特性,使其能夠對應體作最佳化。在編譯時加上-s ASM_JS=1即可。

1
$ ./emcc -s ASM_JS=1 hello.c -o hello.out.js

如果有需要的話可以參考

WebAssembly

最近新推出來的玩意,在2016年各家Browsers終於推出preview版本,而Emscripten更能直接輸出WebAssembly
請參考WebAssembly
另外一個方式就是使用自定義的clang來輸出WebAssembly

其他的參考連結
A Look at Web Assembly and Molecular Analysis
Webassembly initial steps tutorial or how to start with wasm

Reference

看到了Eraser iterators這篇不發表一下文章實在對不起作者啊
把Erase的痛點都寫出來了
在Range based loop要Erase只有以下兩種方法

1
2
3
4
5
6
7
8
9
10
11
for (auto it = array.begin(); it != array.end();)
{
if (*it == delete)
{
it = array.erase(it);
}
else
{
it++;
}
}

或者是

1
2
3
4
5
6
7
array.erase(std::remove_if(array.begin(), array.end(),
[](auto& item)
{
bool deleting = condition;
return deleting;
}
), array.end());

第一種很直覺,不過跟C++11提倡的Range based loop算是一種退化
而第二種出現了4個array,一不小心還會有打錯字的風險
天才作者提出了第三種方式

1
2
3
4
5
for (auto& item : iter::eraser(array))
{
if (*item == value) // Access item via deref
item.mark_for_erase(); // Item is marked for deletion, but is still valid until end of loop iteration
}

非常漂亮的解法!

從Boost Variant談起

1
2
3
#include <boost/variant.hpp>
boost::variant<int, std::string> v;
v = "Hello World!";

boost::get

使用boost::get需要給出正確型別,不然會拋出 Exception

1
2
std::cout << boost::get<std::string>(v) << std::endl; // Hello World!
std::cout << boost::get<int>(v) << std::endl; // terminate called after throwing an instance of 'boost::bad_get'

Use RTTI

1
2
3
4
5
6
7
8
void var_print(const boost::variant<int, std::string> &v)
{
if (v.type() == typeid(int)) {
std::cout << boost::get<int>(v) << std::endl;
} else if (v.type() == typeid(std::string)) {
std::cout << boost::get<std::string>(v) << std::endl;
}
}

每增加一種型別就要修改程式碼,並且影響性能

Visitor Pattern

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class var_visitor : public boost::static_visitor<void>
{
public:
void operator()(int i) const {
std::cout << i << std::endl;
}
void operator()(const std::string& str) const {
std::cout << str << std::endl;
}
// the default case:
template <typename T> void operator()(T const &) const {
std::cout << "FALLBACK: " << __PRETTY_FUNCTION__ << "\n";
}
};
boost::apply_visitor(var_visitor(), v);

不過C++17的visit功能更強,實現更優雅

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class var_visitor
{
public
void operator()(int i) const {
std::cout << i << std::endl;
}
void operator()(const std::string& str) const {
std::cout << str << std::endl;
}
// the default case:
template <typename T> void operator()(T const &) const {
std::cout << "FALLBACK: " << __PRETTY_FUNCTION__ << "\n";
}
};
visit(var_visitor(), v);

智逾期他的visit方式就先打住,有空在研究,介紹variant可以作些什麼

Stack-based run-time polymorphism

傳統基於heap based的polymorphism都是這麼做的
– 分配一塊記憶體
– 將物件創造於記憶體上
– 根據vtbl呼叫virtual function
– 歸還記憶體

所以一般都會寫出這樣的程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct Base {
virtual ~Base() = default;
virtual void func() const = 0;
};
struct Der1 : public Base {
void func() const override { cout << "Der1" << endl; }
};
struct Der2 : public Base {
void func() const override { cout << "Der2" << endl; }
};

unique_ptr<Base> create(int v)
{
if (v)
return make_unique<Der1>();
else
return make_unique<Der2>();
}

auto test = [](const Base &obj) {
obj.func();
};
auto obj = create(0);
test(*obj);

而基於stack的作法,省去了最前面跟最後面的步驟,因此速度更快,如果有機會devirtualize的話,連vtbl都可以不需要

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using derv = variant<Der1, Der2>;
erv create(int v)
{
if (v)
return Der1();
else
return Der2();
}

template <typename BaseType, typename ... Types>
BaseType& cast_to_base(variant<Types ...>& v)
{
return visit([](BaseType& arg) -> BaseType& { return arg; }, v);
}

auto test = [](const Base &obj) {
obj.func();
};
derv obj = create(0);
test(cast_to_base<Base>(obj));

Reference

variant in C++14
浅谈boost.variant的几种访问方式
让boost.variant支持lambda表达式访问
default visitor function for boost::variant
visiting variants using lambdas - part 1
visiting variants using lambdas - part 2
Polymorphism Polymorphism

不得不說,這真的是個炫技,不過還真有用。
原先的程式碼,煩譟又容易錯

1
2
if (thing.x == 1 || thing.x == 2 || thing.x == 3)
dosomething();

可以改寫成這樣
C++11版

1
2
3
4
5
6
7
8
9
template<typename U, typename ... T>
bool one_of(U&& u, T && ... t)
{
bool match = false;
(void)std::initializer_list<bool>{ (match = match || u == t)... };
return match;
}
if (one_of(thing.x, 1, 2, 3))
dosomething();

關劍在Varadic Template Pack expansion,他創造了一個initial_list,其值就是當下match的值。
將initial_list的值印出來更容易看出變化,因此小小變化一下

1
2
3
4
5
6
7
8
9
template<typename U, typename ... T>
bool one_of(U&& u, T && ... t)
{
bool match = false;
auto list = std::initializer_list<bool>{ (match = match || u == t)... };
for (auto v : list)
std::cout << v << std::endl;
return match;
}

部過我們的inital_list根本沒用到, 所以就直接宣告成void讓編譯氣決定最佳化了
C++17版可以寫得更簡單

1
2
3
4
5
template<typename U, typename ... T>
bool one_of(U&& u, T && ... t)
{
return ( (u == t) || ... );
}

Reference

Parameter pack
C++17 Fold Expressions
A Data Point for MSVC vs Clang Code Generation

Problem

目前的主流就是以composition代替inheritance,因此可以寫出這樣的程式碼

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
struct SubCompoent1 {
int v = 100;
int add(int num) { return v + num; }
int sub(int num) { return v - num; }
};
struct SubCompoent2 {
int v = 300;
int mul(int num) { return v * num; }
int div(int num) { return v / num; }
};
struct Component {
Component() : comp1(new SubCompoent1), comp2(new SubCompoent2) {}
std::unique_ptr<SubCompoent1> comp1;
std::unique_ptr<SubCompoent2> comp2;
int add(int num) { return comp1->add(num); }
int sub(int num) { return comp1->sub(num); }
int mul(int num) { return comp2->mul(num); }
int div(int num) { return comp2->div(num); }
};
int func(Component *comp)
{
return comp->add(300);
}
int func1(Component *comp)
{
return comp->div(5);
}

這樣的程式碼很值觀,>不過問題也很明顯,一旦新增一個Function Call,接著又要改個地方

Possible Solution 1

將SubCompent的介面公開,然後提供一組介面存取SubComponent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct Component {
Component() : comp1(new SubCompoent1), comp2(new SubCompoent2) {}
std::unique_ptr<SubCompoent1> comp1;
std::unique_ptr<SubCompoent2> comp2;
SubCompoent1* getComp1() { return comp1.get(); }
const std::unique_ptr<SubCompoent2>& getComp2() { return comp2; }
};
nt func(Component *comp)
{
SubCompoent1* comp1 = comp->getComp1();
return comp1->add(300);
}
int func1(Component *comp)
{
const auto& comp2 = comp->getComp2();
return comp2->div(5);
}

跟上面方式相比,少了每次新增API介面就要修改Component的煩惱,不過要為每組Subcomponent提供一組Access Function,試著將他自動化

Possible Solution 2

結合C++14的get和tuple之後,可以寫成這樣

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct Component {
Component() : comp1(new SubCompoent1), comp2(new SubCompoent2) {}
std::unique_ptr<SubCompoent1> comp1;
std::unique_ptr<SubCompoent2> comp2;
SubCompoent1* getComp1() { return comp1.get(); }
const std::unique_ptr<SubCompoent2>& getComp2() { return comp2; }
};
int func(Component *comp)
{
return comp->get<SubCompoent1>()->add(300);
}
int func1(Component *comp)
{
return comp->get<SubCompoent2>()->div(5);
}

不過這樣還是需要輸出SubComponent的介面,在某些場合底下還是不太適用
不知道未來可不可以靠refelection和overload operator-> 來寫出更好的程式碼