C++ functor

C++ functor

functor

假想有一個函數的功能是將傳進來的參數加一之後回傳:

int add_one(int x){
    return x + 1;
}

如果我們想把該函數的功能改為加二,一種方法是再定義一個同樣的函數,但是把加一改成加二:

int add_two(int x){
    return x + 2;
}

雖然上面這種方法也能達到目的,但總顯得不夠簡潔。

另一種比較簡潔的方法則是定義一個functor(即function object)“類別”。包括三部份:

  1. 定義一個私有成員變數(用來當作"加數")
  2. 建構子接受一個參數,把剛定義的私有成員變數(即"加數")設為該值
  3. overload ()這個運算子:其功能是把參數加上"加數"後回傳
class Adder{
private:
    int addend; //加數
public:
    Adder(int x) : addend(x) {}
    
    int operator() (int augend) const {
        return augend + addend;
    }
};

如此一來,我們每定義一個Adder類別的物件,就能得到為參數加一個特定"加數"的functor,而不必像上一個方法中為每一個"加數"都定義一個函數了:

#include <iostream>

// Driver code 
int main() 
{ 
    int arr[] = {1, 2, 3};
    
    //this is a functor that adds a number by 3
    Adder myadder(3); 
    
    for(int e : arr){
        std::cout << myadder(e) << " ";
    }
    std::cout << std::endl;
    // 4 5 6 

    myadder = Adder(5);
    
    for(int e : arr){
        std::cout << myadder(e) << " ";
    }
    std::cout << std::endl;
    // 6 7 8
} 

有了上述概念後,現在再來看FunctionObject(即functor)的官方定義應該會比較好理解,來自C++ named requirements: FunctionObject

A FunctionObject type is the type of an object 
that can be used on the left of the function call operator.

即,FunctionObject(functor)是一種類型,對於該類型的一個物件f,我們總是可以在它後面接上function call operator,(),以此來調用函數。

TensorRT/samples/common/buffers.h中:

//!          AllocFunc must be a functor that takes in (void** ptr, size_t size)
//!          and returns bool. ptr is a pointer to where the allocated buffer address should be stored.
//!          size is the amount of memory in bytes to allocate.
//!          The boolean indicates whether or not the memory allocation was successful.
//!          FreeFunc must be a functor that takes in (void* ptr) and returns void.
template <typename AllocFunc, typename FreeFunc>
class GenericBuffer
{
public:
    GenericBuffer(size_t size, nvinfer1::DataType type)
        : mSize(size)
        , mCapacity(size)
        , mType(type)
    {
        //調用allocFn的operator()函數來分配記憶體
        if (!allocFn(&mBuffer, this->nbBytes()))
        {
            //failure to allocate storage
            throw std::bad_alloc();
        }
    }

    ~GenericBuffer()
    {
        //調用freeFn的operator()函數來釋放記憶體
        freeFn(mBuffer);
    }

private:
    //...
    void* mBuffer;
    //兩個function object
    AllocFunc allocFn;
    FreeFunc freeFn;
};

class DeviceAllocator
{
public:
    //注意其參數是指標的指標void**
    //overload ()這個operator
    bool operator()(void** ptr, size_t size) const
    {
        return cudaMalloc(ptr, size) == cudaSuccess;
    }
};

class DeviceFree
{
public:
    void operator()(void* ptr) const
    {
        cudaFree(ptr);
    }
};

class HostAllocator
{
public:
    //ptr是指向指標的指標
    bool operator()(void** ptr, size_t size) const
    {
        //分配一塊記憶體,並將其位置指定給ptr指向的指標
        *ptr = malloc(size);
        //確認拿到的記憶體位置非空
        return *ptr != nullptr;
    }
};

class HostFree
{
public:
    void operator()(void* ptr) const
    {
        free(ptr);
    }
};

using DeviceBuffer = GenericBuffer<DeviceAllocator, DeviceFree>;
using HostBuffer = GenericBuffer<HostAllocator, HostFree>;

首先定義 DeviceAllocatorDeviceFreeHostAllocatorHostFree等四個functor。

其次在GenericBuffer中定義了兩個相對應的物件allocFnfreeFn

最後因為()已經被overload過了,所以可以透過 allocFn(&mBuffer, this->nbBytes())freeFn(mBuffer) 來調用。

根據Why use functors over functions?,比起function pointer,使用functor有以下幾個好處:

  • parameterisation:可以簡單地透過參數來讓函數完成不同的功能
  • statefulness:調用functor可以改變functor本身的狀態,影響下回調用的結果
  • separation of concerns:在計算平均值的例子中,將遍歷容器的邏輯及計算平均值的邏輯分開
  • performance:functor能較容易地被編譯器"inline",這能大幅地提升運行速度

但在這個例子中,分配及釋放記憶體的函數都不需要parameterisation,亦非statefulness。那麼為何還要用functor而非function pointer呢?

唯一比較能說服筆者的說法來自Function or functor as template parameter? (If it does not need a state)

If you opted for a function pointer, 
then the function being defined would 
impose a requirement on the users, reducing flexibility.

也就是說,為了程序的彈性考量(日後GenericBuffer可能會需要parameterisation或stateful的函數),所以才採用functor,而非使用靈活性不足的function pointer。

參考連結

Functors in C++

Why use functors over functions?

Function or functor as template parameter? (If it does not need a state)

C++ named requirements: FunctionObject

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值