消除相似函数定义的重复代码
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) const
和 std::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
限定符。
我有一个由一些逻辑 + 底层系统调用组成的方法。现在必须实现另一种方法,它包含完全相似的逻辑,但只有底层系统调用发生变化。
我正在尝试想出一些方法来重用公共代码并实现另一种方法,该方法可能需要一个可调用对象来调用底层系统调用,但自 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) const
和 std::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
限定符。