我的代码中有这种模式:

public:
    int start(const uint32_t channel);
    int stop(const uint32_t channel);
    void set_current_desc(uint32_t channel, uint64_t cdesc);
    // ...
private:
    const uint32_t channel_;

所有函数都接受参数channel。我想提供这个接口:

public:
    int start(const uint32_t channel);
    int start(void) { return start(channel_); } 
    int stop(const uint32_t channel);
    int stop(void) { return stop(channel_); }
    void set_current_desc(uint32_t channel, uint64_t cdesc);
    void set_current_desc(uint64_t cdesc) { set_current_desc(channel_, cdesc); }
    // ...
private:
    const uint32_t channel_;

是否有一种通用方法可以一次性对所有函数执行此操作,还是我必须将其全部输入?我猜存在一些混乱的宏方法,但我更喜欢 C++ 非宏解决方案。

编辑:由于向后兼容,初始界面必须保留。

11

  • 3
    您是否考虑过使用默认函数参数而不是增加这些函数?


    – 

  • 7
    从我们看到的少量代码来看,我认为问题出在设计上。如果方法是类成员,为什么允许将不同的通道传递给方法?如果用户想在不同的通道上操作,为什么不使用不同的通道创建此类的新实例?


    – 

  • 1
    使参数std::optional


    – 

  • 你在哪里设置channel_?也许公开一个 setter 方法来更改其值,那么你可以避免将其传递给每个函数,只需在调用任何函数之前调用 setter 方法(如果需要)。


    – 


  • @alagner:看起来用作channel_默认参数需要成员是静态的。我不确定我是否愿意这样做。@Yksisarvinen:我同意设计不是最优的,但我希望代码保持向后兼容早期用途。@kiner_shah:channel_在构造函数的初始化列表中设置。但如上所述,我更愿意保留已经存在的接口以实现向后兼容。


    – 


最佳答案
3

您可以修改方法以接受具有默认值的

如果调用者未提供通道(通过新接口),channel_则将使用该成员。

如果调用者提供了通道(如您为向后兼容所要求的),则将使用该通道。

这是使用方法完成的

#include <optional>
#include <iostream>
#include <cstdint>

class Test {
public:
    Test(uint32_t channel) : channel_(channel) {}

    void start(std::optional<uint32_t> channel = std::nullopt)
    {
        // Determine `cur_channel` to use:
        uint32_t cur_channel = channel.value_or(channel_);
        // Use `cur_channel` ...
        std::cout << "start, using channel: " << cur_channel << "\n";
    }
private:
    const uint32_t channel_;
};

int main() {
    Test test{ 333 }; // `channel_` member will be 333
    test.start();     // will use the `channel_` member of `test`
    test.start(5);    // will use 5 for the current channel (backwards compatibility)
}

输出:

start, using channel: 333
start, using channel: 5

类似的方法也可用于其余的方法。

您可以编写具有重载的基类channel_,但仍然有相对大量的样板。

struct channel_ops {
    int start(this auto&& self) { return self.start(channel_); } 
    int stop(this auto&& self) { return self.stop(channel_); }
    void set_current_desc(this auto&& self, uint64_t cdesc) { self.set_current_desc(channel_, cdesc); }
protected:
    int channel_;
}

class foo: public channel_ops {
    // ...
public:
    int start(const uint32_t channel);
    using channel_ops::start;
    int stop(const uint32_t channel);
    using channel_ops::stop;
    void set_current_desc(uint32_t channel, uint64_t cdesc);
    using channel_ops::set_current_desc;
    // ...
};

在 C++23 之前,您需要使用 CRTP:

template<typename Self>
struct channel_ops {
    int start() { return static_cast<Self*>(this)->start(channel_); } 
    int stop() { return static_cast<Self*>(this)->stop(channel_); }
    void set_current_desc(this auto& self, uint64_t cdesc) { static_cast<Self*>(this)->set_current_desc(channel_, cdesc); }
protected:
    int channel_;
}

class foo: public channel_ops<foo> {
    // ...
public:
    int start(const uint32_t channel);
    using channel_ops::start;
    int stop(const uint32_t channel);
    using channel_ops::stop;
    void set_current_desc(uint32_t channel, uint64_t cdesc);
    using channel_ops::set_current_desc;
    // ...
};

如果坚持这种设计,一种可能性是使用默认的常规参数值UNDEFINED并依靠它来知道是否start在没有任何参数的情况下调用。

#include <cstdint>
#include <iostream>
#include <limits>
#include <cassert>

class foo
{
public:
    foo (uint32_t channel) : channel_(channel) 
    {
        // UNDEFINED_CHANNEL must be forbidden for channel_
        // otherwise infinite recursion could occur in 'start'
        assert (channel_ != UNDEFINED_CHANNEL);
    }
    
    int start (const uint32_t channel = UNDEFINED_CHANNEL)
    {
        // if no argument is provided, we use 'channel_'
        if (channel == UNDEFINED_CHANNEL)  { return start (channel_); }
        
        // do something here
        std::cout << "input: " << channel << "\n"; 
        
        return 0;
    }

private:
    const uint32_t channel_ {};
    
    static const uint32_t UNDEFINED_CHANNEL = std::numeric_limits<uint32_t>::max(); 
};

int main()
{
    foo x = 123;  
    
    x.start ();
    x.start (1);
    x.start (std::numeric_limits<uint32_t>::max());

    foo y = std::numeric_limits<uint32_t>::max();  // we should have an assert here
    y.start();
}

无论如何,这并不完美,因为您不应该将其用作UNDEFINED参数,start因为它会使用channel_成员值。

您还必须确保UNDEFINED不能使用它,否则如果您不提供任何参数,channel_您可能会出现无限递归。start