У меня есть метод, который состоит из некоторой логики + базовый системный вызов. Теперь должен быть реализован другой метод, который содержит точно такую же логику, но только изменения базового системного вызова.
Я пытаюсь придумать какой-нибудь способ повторно использовать общий код и реализовать другой метод, который может потребовать вызова для вызова базового системного вызова, но не увенчался успехом с самого начала 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;
}
В 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
или вы можете просто захватить эти переменные.
Когда я собираюсь сравнить ваши версии 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);
Таким образом, ваша 1-я версия может быть реорганизована для вызова 2-й версии с использованием определенного набора flags
.
Или вы можете предоставить значение по умолчанию для flags
лайк
std::string Socket::recv(const int bufSize, SF::recv flags = DefaultFlags) const
Самое простое решение — суммировать методы и передать неверный флаг, если вызывается прочитанное сообщение:
if(flags==INVALID)
recvd = ::read(sockfd, buffer.get() + count, bufSize - count);
else
recvd = ::recv(sockfd, buffer.get() + count, bufSize - count, f);
НО, это нарушит Принцип единой ответственности, потому что метод теперь имеет две обязанности и две причины для изменения.
Лучшим решением будет извлечь общие части обоих методов.
read() {
commonMethod1();
::read();
commonMethod2();
}
write() {
commonMethod1();
::read();
commonMethod2();
}
Тем не менее, ваш вопрос может быть основан на разных мнениях, но этот мой. 😉
Один простой способ — добавить частную вспомогательную функцию в 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);
}
Обратите внимание, что я также удалил избыточный const
квалификатор из аргументов, передаваемых по значению.