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

时间:2017-02-11 07:21:06

标签: c++ networking refactoring c++14 code-reuse

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

我正在尝试某种方式来重用公共代码并实现另一种方法,该方法可能需要一个可调用来调用底层系统调用,但由于read和{{ 1}}来电不同。

很高兴能找到一个优雅的解决方案。方法看起来像 -

第一个功能

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;
}

4 个答案:

答案 0 :(得分:3)

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

的版本时

enter image description here 唯一的区别是

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

答案 1 :(得分:2)

在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,或者您可以简单地捕获这些变量。

答案 2 :(得分:1)

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

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();
}

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

答案 3 :(得分:1)

一种简单的方法是在library(clpfd)

中添加私有帮助函数
Socket

并将其实现为

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); } 限定符。