消除相似函数定义的重复代码

eliminate duplicate code for similar function definitions

我有一个由一些逻辑 + 底层系统调用组成的方法。现在必须实现另一种方法,它包含完全相似的逻辑,但只有底层系统调用发生变化。

我正在尝试想出一些方法来重用公共代码并实现另一种方法,该方法可能需要一个可调用对象来调用底层系统调用,但自 read 以来一直没有成功并且recv调用不同。

如果能找到一个大致相同的优雅解决方案,那就太好了。这些方法看起来像 -


第一个函数

std::string Socket::read(const int bufSize) const
{
    auto buffer = std::make_unique<char[]>(bufSize + 1);
    auto recvd = 0, count = 0;

    std::string str;
    str.reserve(bufSize);

    do {

        // ONLY THIS PART IS DIFFERENT
        recvd = ::read(sockfd, buffer.get() + count, bufSize - count);
        // ONLY THIS PART IS DIFFERENT

        count += recvd;
        if (count == bufSize) {
            str.append(buffer.get());
            str.reserve(str.length() + bufSize);
            std::memset(buffer.get(), 0, bufSize);
            count = 0;
        }
    } while (recvd > 0);

    str.append(buffer.get(), count);

    if (recvd == -1) {
        // TODO: Check for recvd == EAGAIN or EWOULDBLOCK and
        // don't throw exception in that case.
        throw std::runtime_error("Error occurred while writing message");
    }

    return str;
}

第二个函数

std::string Socket::recv(const int bufSize, SF::recv flags) const
{
    auto buffer = std::make_unique<char[]>(bufSize + 1);
    auto recvd = 0, count = 0;

    std::string str;
    str.reserve(bufSize);

    do {

        // ONLY THIS PART IS DIFFERENT
        const auto f = static_cast<int>(flags);
        recvd = ::recv(sockfd, buffer.get() + count, bufSize - count, f);
        // ONLY THIS PART IS DIFFERENT

        count += recvd;
        if (count == bufSize) {
            str.append(buffer.get());
            str.reserve(str.length() + bufSize);
            std::memset(buffer.get(), 0, bufSize);
            count = 0;
        }
    } while (recvd > 0);

    str.append(buffer.get(), count);

    if (recvd == -1) {
        // TODO: Check for recvd == EAGAIN or EWOULDBLOCK and
        // don't throw exception in that case.
        throw std::runtime_error("Error occurred while writing message");
    }

    return str;
}

当我要比较你的 std::string Socket::read(const int bufSize) conststd::string Socket::recv(const int bufSize, SF::recv flags) const 版本时

唯一的区别是

const auto f = static_cast<int>(flags);

recvd = ::recv(sockfd, buffer.get() + count, bufSize - count, f);

因此,您的第一个版本可以重构为使用一组特定的 flags.

的第二个版本的调用

或者您可以为 flags 提供默认值,例如

 std::string Socket::recv(const int bufSize, SF::recv flags = DefaultFlags) const

在 C++14 中你可以这样做。这是一个更灵活的解决方案,尽管它并不是为满足您的确切需求而设计的。结果,它的表现力可能会降低。

#include <utility>

namespace detail {

template <class Fn, class... Args>
auto DuplicatedCode(Fn &&fn, Args&&... args) {
  // some code

  // auto result =
  std::forward<Fn>(fn)(std::forward<Args>(args)...);

  // more code

  // return
}

}

void foo() {
  detail::DuplicatedCode([](){return 0;});
}

void bar() {
  detail::DuplicatedCode([](){return 1;});
}

您可以在 foo 和 bar 中声明一些局部变量,DucplicatedCode 会将它们转发给 fn 或者您可以简单地捕获这些变量。

最简单的解决方案是对方法求和并传递一个无效标志,如果读取的消息被调用:

if(flags==INVALID)
        recvd = ::read(sockfd, buffer.get() + count, bufSize - count);
    else
        recvd = ::recv(sockfd, buffer.get() + count, bufSize - count, f);

但是,这将违反 Single Responsibility Principle,因为该方法现在有两个职责和两个更改原因。

更好的解决方案是提取两种方法的共同部分。

read() {
 commonMethod1();
 ::read();
 commonMethod2();
} 
write() {
 commonMethod1();
 ::read();
 commonMethod2();
}

不过,您的问题可能基于不同的意见,但这个是我的。 ;-)

一种简单的方法是在 Socket class

中添加一个私有辅助函数
class Socket
{
     // everything else you already have

     private:

        std::string recv_helper(int bufSize, const SF::recv *flags = nullptr) const;
              // note the second argument is a pointer  
};    

并将其实现为

std::string Socket::recv_helper(int bufSize, const SF::recv *flags) const
{
     //   All the code preceding your first // ONLY THIS PART IS DIFFERENT

     if (flags)
     {
         recvd = ::recv(sockfd, buffer.get() + count, bufSize - count,
                        static_cast<int>(*flags));
     }
     else
     {
         recvd = ::read(sockfd, buffer.get() + count, bufSize - count);
     }

      //   All the code following your second // ONLY THIS PART IS DIFFERENT    
}

那么你需要做的就是重新实现你的两个函数来调用助手。

std::string Socket::read(int bufSize) const
{
    return recv_helper(bufSize);
}

std::string Socket::recv(int bufSize, SF::recv flags) const
{
    return recv_helper(bufSize,  &flags);
}

请注意,我还从按值传递的参数中删除了多余的 const 限定符。