# 簡介 C++ 的 Type Erase (用多型和模板做 Duck Type)

## 起點

讓我們先從 template 出發：foo 需要一個 callback function。

```cpp
template<typename Func>
void foo(Func callback) {
    // ...
    callback();
}
```

但是這會讓編譯錯誤訊息有點模糊：假如 callback 並不是一個可以呼叫的函數指標，或者並不是一個 callable object ，那編譯器會說錯出在第四行。但是我們都希望，編譯器在呼叫函數時就幫我們指出：這不是 foo 想要的 callback 參數。

STL 裡面就有提供 `std::function<void()>` 可以存下 foo 所需要的 callback，這就會倒出一個問題：我們要怎麼做出 `std::function<void()>` ？

以下先忽略 private 包裝和 move，這些不是這文章的重點。

## 用 class 包裝?

我們先捏一個 struct:

```cpp
template <typename Func>
struct Callable {
    Func func_;
    explicit Callable(Func func) : func_(func) { }

    void operator()() {
        func_();
    }
};
```

可以用 Callable 包起來的 Func ，肯定可以被我們用 `func_()` 的方式呼叫，可是這仍然沒辦法當作一個完整 Type – 我們仍然缺一個 template parameter。

## 多型?

但我們可以在上面再加蓋一層:

```cpp
struct CallableType {
    CallableType() = default;
    virtual ~CallableType() = default;
    virtual void operator()() = 0;
};

template <typename Func>
struct Callable : public CallableType {
    Func func_;
    Callable(Func func) : func_(func) { }
    void operator()() {
        func_();
    }
};
```

要使用 CallableType 的話仍然需要一層指標讓多型 work，這樣用起來很麻煩。所以我們再包一層：CallableObj，並且讓型別在 constructor 就自動辨別好。

```cpp
struct CallableObj {
    std::unique_ptr<CallableType> callableType_;

    template <typename Func>
    CallableObj(Func func)
        : callableType_(std::make_unique<Callable<Func>>(func)) { }

    void operator()() {
        (*callableType_)();
    }
};
```

這時，我們便可以用 CallableObj 當作 foo 的參數了! … 嗎？？？

```cpp
void foo(Func callback) {
    // ...
    callback();
}
```

## Copy

因為 CallableObj 裡面有一個 `std::unique_ptr<CallableType>`，所以 CallableObj 是複製不了的。

```cpp
function "CallableObj::CallableObj(const CallableObj &)"
(declared implicitly) cannot be referenced -- it is a deleted function
```

我們需要繼承 CallableType 的 Callable&lt;Func&gt; 提供一個 clone() 的介面，讓我們可以好好的複製被 CallableType 藏起來的東西，也就是 Func。

```cpp
struct CallableType {
    // ...
    virtual std::unique_ptr<CallableType> clone() const = 0;
};

template <typename Func>
struct Callable : public CallableType {
    // ...
    // just copy
    std::unique_ptr<CallableType> clone() const override {
        return std::make_unique<Callable<Func>>(*this);
    }
};
```

所以我們就可以把 CallableObj 的 copy constructor/assignment 補上

```cpp
CallableObj(const CallableObj& other):
    callableType_(other.callableType_->clone()) { }

CallableObj& operator=(const CallableObj& other) {
    CallableObj tmp(other);
    callableType_.swap(tmp.callableType_);
    return *this;
}
```

## Code

最後，再補上 move constructor/assignment 和 null pointer check。

Code:

```cpp
#include <iostream>
#include <memory>

struct CallableType {
    CallableType() = default;
    virtual ~CallableType() = default;
    virtual void operator()() = 0;
    virtual std::unique_ptr<CallableType> clone() const = 0;
};

template <typename Func>
struct Callable : public CallableType {
    Func func_;
    explicit Callable(Func func) : func_(func) { }

    void operator()() {
        func_();
    }

    std::unique_ptr<CallableType> clone() const override {
        return std::make_unique<Callable<Func>>(*this);
    }
};

struct CallableObj {
    std::unique_ptr<CallableType> callableType_;

    template <typename Func>
    CallableObj(Func func)
        : callableType_(std::make_unique<Callable<Func>>(func)) { }

    ~CallableObj() = default;
    CallableObj(CallableObj&&) = default;
    CallableObj& operator=(CallableObj&&) = default;

    CallableObj(const CallableObj& other) {
        if (other.callableType_) {
            callableType_ = other.callableType_->clone();
        }
    }

    CallableObj& operator=(const CallableObj& other) {
        CallableObj tmp(other);
        callableType_.swap(tmp.callableType_);
        return *this;
    }

    void operator()() {
        if (callableType_) {
            (*callableType_)();
        }
    }
};

void foo(CallableObj callback) {
    callback();
}

int main() {
    CallableObj co([]() { std::cout << "hello\n"; });
    foo(co);              // output hello
    foo(std::move(co));   // output hello
    foo(co);              // no output
    return 0;
}
```

## More...?

所以，還有那些沒做到？

1. Private 包裝：func\_ 和 callableType\_ 應該要是 private member
    
2. Func 要考慮 move semantic
    
3. 要怎麼用 Template 做到 `CallableObj<RetType(ArgType1, ArgType2, ...)>`
