0%

我只是個Javascript初新者,寫出來的可能也會務人子弟。
只是對Javascript的Asynchronous Programming Model有興趣,紀錄一下。

Callback Hell

在最初的Javascript的設計,程式只能這樣寫。一層又一層的Callback。不蛋難以維護,並且容易出錯。

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
function wait(func, val) {
return setTimeout(() => {
func(val);
}, 1000);
}
function asyncTask() {
var val = 0;
wait((val) => {
console.log("Step 1");
console.log(val);
wait((val) => {
console.log("Step 2");
console.log(val);
wait((val) => {
console.log("Step 3");
console.log(val);
wait((val) => {
console.log("Finish");
console.log(val);
}, val + 4);
}, val + 3);
}, val + 2);
}, val + 1);
}

asyncTask();

ES6之後引季Promise,可以將Callback從水平拓展變成垂直拓展,讓程式碼更像是一般Synchronous Programming的方式

Promise

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
38
39
40
41
42
43
44
function wait(func, val) {
return setTimeout(() => {
func(val);
}, 1000);
}
function finish(val) {
console.log("Finish");
console.log(val);
}
function step3(val) {
console.log("Step 3");
console.log(val);
}
function step2(val) {
console.log("Step 2");
console.log(val);
}
function step1(val) {
console.log("Step 1");
console.log(val);
}
function waitPromise(func, val) {
return new Promise((resolve, reject) => {
wait(() => {
func(val);
resolve(val);
});
});
}
function asyncTask() {
var val = 0;
return waitPromise(step1, val + 1).then((val) => {
return waitPromise(step2, val + 2);
}).then((val) => {
return waitPromise(step3, val + 3);
}).then((val) => {
return waitPromise(finish, val + 4);
});
}

asyncTask();


asyncTask();

Generator

不過ES6同樣引入了Generator,可以用Generator模擬Synchronous Programming的方式
asyncTaskFlow是個Generator就是控制流,只是我們要以這個順序執行
比起Promise更像是我們熟悉的方式

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
function wait(func) {
return setTimeout(func, 1000);
}
function finish() {
console.log("Finish");
}
function step3() {
console.log("Step 3");
}
function step2() {
console.log("Step 2");
}
function step1() {
console.log("Step 1");
}
function* asyncTaskFlow() {
yield wait(step1);
yield wait(step2);
yield wait(step3);
yield wait(finish);
}
function asyncTask() {
var gen = asyncTaskFlow();
for (v of gen) ;
}

asyncTask();

Async & Await

跟C#一樣,用Async和Awit作為最終解決方案,雖然這還處在ES7 Draft,不過日後應該會被採用
可以參考 ES7 async functionsSimplifying Asynchronous Coding with ES7 Async Functions

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
38
39
40
41
42
43
async function finish(val) {
return new Promise((resolve, reject) => {
wait(() => {
console.log("Finish");
resolve(val + 4);
});
});
}
async function step3(val ) {
return new Promise((resolve, reject) => {
wait(() => {
console.log("Step 3");
resolve(val + 3);
});
});
}
async function step2(val) {
return new Promise((resolve, reject) => {
wait(() => {
console.log("Step 2");
resolve(val + 2);
});
});
}
async function step1(val) {
return new Promise((resolve, reject) => {
wait(() => {
console.log("Step 1");
resolve(val + 1);
});
});
}
async function asyncTask() {
var result = await step1(0);;
console.log(result);
result = await step2(result);
console.log(result);
result = await step3(result);
console.log(result);
result = await finish(result);
console.log(result);
}
asyncTask();
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
38
39
40
41
42
43
44
45
46
47
48
49
50
function wait(func) {
return setTimeout(func, 1000);
}
async function finish(val) {
return new Promise((resolve, reject) => {
wait(() => {
console.log("Finish");
resolve(val + 4);
});
});
}
async function step3(val ) {
return new Promise((resolve, reject) => {
wait(() => {
console.log("Step 3 failed");
reject("Cancel operation");
});
});
}
async function step2(val) {
return new Promise((resolve, reject) => {
wait(() => {
console.log("Step 2");
resolve(val + 2);
});
});
}
async function step1(val) {
return new Promise((resolve, reject) => {
wait(() => {
console.log("Step 1");
resolve(val + 1);
});
});
}
async function asyncTask() {
try {
var result = await step1(0);;
console.log(result);
result = await step2(result);
console.log(result);
result = await step3(result);
console.log(result);
result = await finish(result);
console.log(result);
} catch (err) {
console.log(err);
}
}
asyncTask();

有時候想要在Linux的Terminal想要印出彩色字串方便,可以有以下方法

C Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#define ANSI_COLOR_RED "\x1b[31m"
#define ANSI_COLOR_GREEN "\x1b[32m"
#define ANSI_COLOR_YELLOW "\x1b[33m"
#define ANSI_COLOR_BLUE "\x1b[34m"
#define ANSI_COLOR_MAGENTA "\x1b[35m"
#define ANSI_COLOR_CYAN "\x1b[36m"
#define ANSI_COLOR_RESET "\x1b[0m"
};

int main (int argc, char const *argv[]) {

printf(ANSI_COLOR_RED "This text is RED!" ANSI_COLOR_RESET "\n");
printf(ANSI_COLOR_GREEN "This text is GREEN!" ANSI_COLOR_RESET "\n");
printf(ANSI_COLOR_YELLOW "This text is YELLOW!" ANSI_COLOR_RESET "\n");
printf(ANSI_COLOR_BLUE "This text is BLUE!" ANSI_COLOR_RESET "\n");
printf(ANSI_COLOR_MAGENTA "This text is MAGENTA!" ANSI_COLOR_RESET "\n");
printf(ANSI_COLOR_CYAN "This text is CYAN!" ANSI_COLOR_RESET "\n");
return 0;
}

這方法簡單,不過麻煩的是要手動加上Color跟Reset標籤在文字前後。

C++11 Solution

突然想到可以用C++11的新特性User defined literal來簡化,可以減少不少手動置入的風險。也可以練習User defined literal的如何使用。

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
#include <stdio.h>
#include <string>
namespace {
#define ANSI_COLOR_RED "\x1b[31m"
#define ANSI_COLOR_GREEN "\x1b[32m"
#define ANSI_COLOR_YELLOW "\x1b[33m"
#define ANSI_COLOR_BLUE "\x1b[34m"
#define ANSI_COLOR_MAGENTA "\x1b[35m"
#define ANSI_COLOR_CYAN "\x1b[36m"
#define ANSI_COLOR_RESET "\x1b[0m"


#define DEFINE_COLOR_STRING(color) \
std::string operator"" _##color (const char* str) \
{ \
std::string tmp(ANSI_COLOR_##color); \
tmp += str; \
tmp += ANSI_COLOR_RESET; \
return tmp;\
}
DEFINE_COLOR_STRING(RED);
DEFINE_COLOR_STRING(GREEN);
DEFINE_COLOR_STRING(YELLOW);
DEFINE_COLOR_STRING(BLUE);
DEFINE_COLOR_STRING(MAGENTA);
DEFINE_COLOR_STRING(CYAN);
};
std::cout << 123_RED << 456_YELLOW << 789_BLUE << std::endl;

不過User Defined Literal前的字串似乎不能寫成”123 456”_RED這種形式。有點可惜

```

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template <typename T, std::size_t N>
class ArrayResult {
constexpr static std::size_t size_ = N;
T data_[N] {};
public:
constexpr std::size_t size() const { return N; }
constexpr T& operator[](std::size_t n) { return data_[n]; }
constexpr const T& operator[](std::size_t n) const { return data_[n]; }
using iterator = T*;
using const_iterator = const T*;
constexpr iterator begin() { return &data_[0]; }
constexpr const_iterator begin() const { return &data_[0]; }
constexpr iterator end() { return &data_[N]; }
constexpr const_iterator end() const { return &data_[N]; }
};

好久沒寫文章了,把之前看到的資料記一記。馬有失蹄,人有錯手,在怎麼宣告不用Debugger的Programming language,可能也需要gdb搭配使用,這邊紀錄一下如何用gdb來幫助golan除錯。
範例程式

1
2
3
4
5
6
7
8
9
10
11
12
package main

import (
"fmt"
)

func main() {
for i := 0; i < 5; i++ {
fmt.Println("Loop")
}
fmt.Println("Done")
}

編譯時間入debuu info

1
$ go build -gcflags "-N -l" test.go

積著就可以照

1
2
$ gdb test
$ b main.main

之後就能夠像之前一樣使用gdb了

Reference

Debugging Go Code with GDB
– [Debugging Go (golang) programs with gdb ]http://thornydev.blogspot.tw/2014/01/debugging-go-golang-programs-with-gdb.html)
Introduction to Go Debugging with GDB
Using the gdb debugger with Go

Macro這東西很早就有了,不過各有巧妙不同。
在Assembler的年代就已經有Macro了

1
2
3
4
COPY            macro   Dest, Source
mov ax, Source
mov Dest, ax
endm

而在C語言的時候,Macro的被使用了更頻繁了

1
#define min(a, b) ((a < b) ? a : b)

C語言的Macro也很簡單,就只是作文字的代換而已。Side Effect除外,出錯的話也很難Debug,因此其他語言打算因此作改進。

Define group of functions

1
2
3
4
5
6
7
8
9
10
11
12
13
#define CK_PR_FENCE(T, I)                               \
CK_CC_INLINE static void \
ck_pr_fence_strict_##T(void) \
{ \
__asm__ __volatile__(I ::: "memory"); \
}

CK_PR_FENCE(atomic, "sfence")
CK_PR_FENCE(atomic_store, "sfence")
CK_PR_FENCE(atomic_load, "mfence")
CK_PR_FENCE(store_atomic, "sfence")
CK_PR_FENCE(load_atomic, "mfence")
CK_PR_FENCE(load, "lfence")

在這情況下 Templateˊ無能為力.

Rust

Macro overload

1
2
3
4
5
6
7
8
9
10
11
12
13
macro_rules! sayHello {
() => {
println!("sayHello");
};
($name:ident) => {
println!("sayHello {:?}", stringify!($name));
};
}
fn main() {
sayHello!();
sayHello!(ABC);

}

Pattern Matching

1
2
3
4
5
6
7
8
9
macro_rules! min {
// base case
($x:expr) => ($x);
// `$x` followed by at least one `$y,`
($x:expr, $($y:expr),+) => (
// call min! on the tail `$y`
std::cmp::min($x, min!($($y),+))
)
}

Recursive Macro

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
macro_rules! write_html {
($w:expr, ) => (());

($w:expr, $e:tt) => (write!($w, "{}", $e));

($w:expr, $tag:ident [ $($inner:tt)* ] $($rest:tt)*) => {{
write!($w, "<{}>", stringify!($tag));
write_html!($w, $($inner)*);
write!($w, "</{}>", stringify!($tag));
write_html!($w, $($rest)*);
}};
}

fn main() {
use std::fmt::Write;
let mut out = String::new();

write_html!(&mut out,
html[
head[title["Macros guide"]]
body[h1["Macros are the best!"]]
]);

assert_eq!(out,
"<html><head><title>Macros guide</title></head>\
<body><h1>Macros are the best!</h1></body></html>");
}

Nim

Reference

Macros - Rust Documentation
A Quick Intro to Rust Macros
Module macros - Nim
Nim Manual

雖然這些東西網路上都找得到,不過為了節省時間,把相關步驟一次記下來。

UsersFetchingSource

有些時刻/usr/src是空的,所以需要手動獲取程式碼

1
2
$ pkg install svnup
$ svnup stable -h svn.freebsd.org

The Configuration File

修改自己需要的Configuration File

1
2
$ cd /usr/src/sys/amd64/conf
$ cp GENERIC MYKERNEL

Building and Installing a Custom Kernel

1
2
3
$ cd /usr/src
$ make buildkernel KERNCONF=MYKERNEL
$ make installkernel KERNCONF=MYKERNEL

Reference:

Create Hadoop Account

1
2
3
$ sudo useradd -m hadoop -s /bin/bash
$ sudo passwd hadoop
$ sudo adduser hadoop sudo

Setup ssh environemtn

1
2
3
4
$ sudo apt-get install openssh-server
$ cd ~/.ssh/
$ ssh-keygen -t rsa
$ cat id_rsa.pub >> authorized_keys

Instal Java

1
$ sudo apt-get install openjdk-7-jre openjdk-7-jdk

修改~/.bashrc押入

export JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64

1
$ source ~/.bashrc

Install Hadoop

1
2
3
$ sudo tar -zxvf ./hadoop-2.7.0.tar.gz -C /usr/local
$ sudo mv ./hadoop-2.7.0/ ./hadoop
$ sudo chown -R hadoop:hadoop ./hadoop

單機Hadoop建立

執行測試程, ˊ執行時記得不能有output`目錄存在

1
2
3
4
$ sudo mkdir input
$ sudo cp README.txt input
$ bin/hadoop jar share/hadoop/mapreduce/sources/hadoop-mapreduce-examples-2.7.0-sources.jar org.apache.hadoop.examples.WordCount input output
$ cat output/*

偽分布式Hadoop建立

修改/usr/local/hadoop/etc/hadoop/core-site.xml

1
2
3
4
5
6
7
8
9
10
11
<configuration>
<property>
<name>hadoop.tmp.dir</name>
<value>file:/usr/local/hadoop/tmp</value>
<description>Abase for other temporary directories.</description>
</property>
<property>
<name>fs.defaultFS</name>
<value>hdfs://localhost:9000</value>
</property>
</configuration>

修改/usr/local/hadoop/etc/hadoop/hdfs-site.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<configuration>
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
<property>
<name>dfs.namenode.name.dir</name>
<value>file:/usr/local/hadoop/tmp/dfs/name</value>
</property>
<property>
<name>dfs.datanode.data.dir</name>
<value>file:/usr/local/hadoop/tmp/dfs/data</value>
</property>
</configuration>

###請動Hadoop
進行Namenode格式化

1
$ bin/hadoop  namenode -format

啟動Guardian process

1
s bin/start-dfs.sh

可以連到 http://localhost:50070 來觀察adoop狀態

俄式偽分布式Hadoop

1
2
3
4
$ bin/hdfs dfs -mkdir -p /user/hadoop
$ bin/hdfs dfs -put README.txt input
$ bin/hadoop jar share/hadoop/mapreduce/sources/hadoop-mapreduce-examples-2.7.0-sources.jar org.apache.hadoop.examples.WordCount input output
$ bin/hdfs dfs -cat output/*

Reference

長知識了,直接看Code比較快

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
int calc(std::exception_ptr& eptr)
{
try {
std::string s;
s.at(0); // throw out of range exception
} catch (...) {
eptr = std::current_exception();
}
return 0;
}
int main()
{
std::exception_ptr eptr;
auto f = std::async(std::launch::async, std::bind(calc, std::ref(eptr)));
try {
int i = f.get();
if (eptr) {
std::rethrow_exception(eptr);
}
std::cout << i << std::endl;
} catch (const std::exception& e) {
std::cout << e.what() << std::endl;
}
return 0;
}ion_ptr eptr;
auto f = std::async(std::launch::async, std::bind(calc, std::ref(eptr)));
try {
int i = f.get();
if (eptr) {
std::rethrow_exception(eptr);
}
std::cout << i << std::endl;
} catch (const std::exception& e) {
std::cout << e.what() << std::endl;
}
return 0;
}

如果要在Thread傳輸exception的食,記得紀錄一個exception_ptr,當exception發生食,用current_exception()捕捉在當今thread發生的excetipn。然後其他Thread發現這個eptr存在的話,透過rethrow_exception處理這個exception。
不過這樣作一點都不直覺啊

Better Solution

搭配同時加入C++11的thread和future,可以寫出更加優雅的Code,promise不僅解決了exception的問題,同時也提供了Thread return value的問題>

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
void calc(std::promise<int> && p)
{
try {
std::string s;
s.at(0); // throw out of range exception
p.set_value(0);
} catch (...) {
p.set_exception(std::current_exception());
}
}
int main()
{
std::promise<int> p;
std::future<int> f = p.get_future();
std::thread thd(calc, std::move(p));
try {
int i = f.get();
std::cout << i << std::endl;
} catch (const std::exception& e) {
std::cout << e.what() << std::endl;
}
thd.join();
return 0;
}

‵``

這故事說起來還真不簡單,在寫Code的時候,往往與這兩樣東西牽扯不清。
例如以下這段Code

1
2
3
FILE *fp = fopen( "input.txt", "r");
if (fp == NULL)
printf (" err %d \n", errno);

fp代表著回傳的正常值,errno表示萬一錯誤的時候,可以供判斷的錯誤依據。
不過就算是return value跟error code,也有很長故事可以說。
$ f(x)=\frac{100}{\frac{100}{x}+1} $當我們的範例,來討論各種處理return value跟error handleing

Global state

在設計API的時候,就完全沒考慮過Error這件事,如同上面的errno那樣,我們可以仿照使用一個global variable。
可能的作法如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int _errno;
const int invalid_value = -1;
int g(int x) {
if (x == 0) {
_errno = -1;
return invalid_value;
}
return 100 / x + 1;
}
int f(int x)
{
int v1 = g(x);
if (v1 == invalid_value)
return v1;
if (v1 == 0) {
_errno = -2;
return invalid_value;
}
return 100 / v1;
}

這作法不難懂,不過有幾個很重大的缺點
– 需要對Return Value定義一個特蘇的值,代表這個值是無效的,而這個Return Value有可能跟值域的某個數值碰撞。例如上例的f(-50),你不知道他是錯誤還是有效值。
– global variable表示任何人都有機會更動到,因此在Code的Maintain跟Protect上難很多
– Error很容易被忽略,因為沒有強制性,可能在實做內部,或是外部使用,都有可能因為Code的修改而跳過Error Code處理

因此有了改良版的方法

Return Error and Value simultaneously

上面方法的改良版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int g(int x, int *retvalue) {
if (x == 0)
return -1;
*retvalue = 100 / x + 1;
return 0;
}
int f(int x, int *retvalue)
{
int err = g(x, retvalue);
if (err != 0)
return err;
if (*retvalue == 0)
return -2;
*retvalue = 100 / (*retvalue);
return 0;
}

這邊的寫法類似於COM的作法,把return value放在最後一個參數,而errorcode當作return值傳回。
也可以像golang那樣傳回多個回傳質的方法,大同小異

1
2
3
4
f, err := os.Open("filename.ext")
if err != nil {
log.Fatal(err)
}

這方是解決了上述錢兩個問題,不過還是無法解決第三個問題

Exception-Based solution

越來越多程式語言都加入當標準配備了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int g(int x) {
if (x == 0)
throw std::exception("cannot be zero in g");
return 100 / x + 1;
}
int f(int x)
{
int v1 = g(x);
if (v1 == 0)
throw std::exception("cannot be zero in f");
return 100 / v1;
}
try {
f(0);
}
catch (std::exception e) {
}

比起上述兩種,exception的優點
– 不需要特異定義一堆內部的Error Code Value,並且Code的可讀性比上面兩者都強。可以專住在Logic上的Code。
而缺點是:
– 效能問題,雖然這問題越來越不重要,不過使用Exception的方案通常比Error code慢。
– Boundary issue,當跨越兩個Shared library的程式碼互動食,這方案完全派不上用場。還是要走回上面的老路。

Railway Oriented Programming

錢幾個作法都是遇到問題就往上拋,而ROP反其道而行,將錯誤往後傳,最後統一處理。

這例子改用ustㄉ做示範範例 因為C++诶有ML系Language Pattern Matching和Abstract Data Type,要模擬這個不如直接換個語言寫寫看當練習。

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
enum Result {
Value(i32),
Error(&'static str)
}
fn div(x: Result, y: Result) -> Result {
match y {
Result::Value(0) => Result::Error("Cannot divide 0"),
Result::Value(y_) => {
match x {
Result::Value(x_) => Result::Value(x_ / y_),
_ => x
}
},
_ => y
}
}
fn add1(x: Result) -> Result {
match x {
Result::Value(v) => Result::Value(v + 1),
_ => x
}
}
fn f(x: i32) -> Result {
return div(Result::Value(100), add1(div(Result::Value(100), Result::Value(x))));
}
fn main() {
let value = f(25);
match value {
Result::Value(v) => println!("The result value is {}", v),
Result::Error(str) => println!("Error happen, {}", str)
}
}

座個事情很簡單,如果Result是有效的值就繼續往下做,不然就直接往後傳。

Enhance Monad solution for Railway Oriented Programming

網路上有很多Monad的介紹,於是東施校憑寫了一個山寨版的。關鍵在於所謂的bind函數,這裡就不多介紹了。

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
trait Monad {
fn unit(x: i32) -> Self;
fn bind(&self, f: fn(i32) -> Self) -> Self;
}
enum Result {
Value(i32),
Error(&'static str)
}
impl Monad for Result {
fn unit(x: i32) -> Result {
Result::Value(x)
}
fn bind(&self, f: fn(i32) -> Self) -> Self {
match *self {
Result::Value(x) => f(x),
Result::Error(str) => Result::Error(str)
}
}
}
fn div100(x: i32) -> Result {
match x {
0 => Result::Error("Cannot divide 0"),
_ => Result::Value(100 / x)
}
}
fn add1(x: i32) -> Result {
Result::Value(x + 1)
}
fn f(x: i32) -> Result {
Result::unit(x).bind(div100).bind(add1).bind(div100)
}
~

Reference

C++ Exceptions: Pros and Cons
http://fsharpforfunandprofit.com/rop/

std::decay

如名稱所說,將所獲取到的類型作退化的動作。
先移除類型T的Reference,得累類型U

  • 如果 is_array::value 為 treu,修改類型type為remove_extent::type *
  • 如果 is_function::value 為 true,修改類型type為add_pointer::type
  • 否則,修改類型type為remove_cv::type
    不過decay可以用來作些什麼?

    將參數以值的方式傳回

    N2609提了一種用法
    1
    2
    3
    4
    5
    template <class T1, class T2> 
    inline pair<T1,T2> make_pair(T1 x, T2 y)
    {
    return pair<T1,T2>(x, y);
    }
    這段Code能動,不過會多出複製物件的成本
    1
    std::pair<std::string, int> p = make_pair("foo", 0);
    如果我們改用Refernece的傳法重寫的話
    1
    2
    3
    4
    5
    template <class T1, class T2> 
    inline pair<T1,T2> make_pair(T1&& x, T2&& y)
    {
    return pair<T1,T2>(x, y);
    }
    以上麵這個範例的話,T1會被推導成const char[4],而不是const char *。因此我們需要Decay
    正確的寫法類似這樣
    1
    2
    3
    4
    5
    6
    7
    8
    template <class T1, class T2> 
    inline pair< typename decay<T1>::type, typename decay<T2>::type >
    make_pair(T1&& x, T2&& y)
    {
    return pair< typename decay<T1>::type,
    typename decay<T2>::type >(std::forward<T1>(x),
    std::forward<T2>(y));
    }

    之前的make_resource範例

    1
    2
    3
    4
    5
    6
    7
    8
    template<typename Creator, typename Destructor, typename... Arguments>
    auto make_resource(Creator c, Destructor d, Arguments&&... args)
    {
    auto r = c(std::forward<Arguments>(args)...);
    if (!r) { throw std::runtime_error{ "Unable to create resource" }; }
    typedef typename std::decay<decltype(*r)>::type ResourceType;
    return std::unique_ptr<ResourceType, void(*)(ResourceType*)>(r, d);
    }
    為了decay推導出primitive type,將變化包裝起來不讓外界知道。

std::result_of

看了Stackoverflow,覺得這東西可以完全用decltype去代。有興趣的話可以參考CppReference的用法。

std::enable_if

SFINAE