исключить дублирование кода для похожих определений функций

У меня есть метод, который состоит из некоторой логики + базовый системный вызов. Теперь должен быть реализован другой метод, который содержит точно такую ​​же логику, но только изменения базового системного вызова.

Я пытаюсь придумать какой-нибудь способ повторно использовать общий код и реализовать другой метод, который может потребовать вызова для вызова базового системного вызова, но не увенчался успехом с самого начала 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;
}

1

Решение

В 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

Другие решения

Когда я собираюсь сравнить ваши версии 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
3

Самое простое решение — суммировать методы и передать неверный флаг, если вызывается прочитанное сообщение:

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

Тем не менее, ваш вопрос может быть основан на разных мнениях, но этот мой. 😉

1

Один простой способ — добавить частную вспомогательную функцию в 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 квалификатор из аргументов, передаваемых по значению.

1
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector