2017-02-11 3 views
3

J'ai une méthode qui est composée d'une logique + un appel système sous-jacent. Maintenant, une autre méthode qui contient exactement la même logique mais uniquement les changements d'appel système sous-jacents doit être implémentée. J'essaie de trouver un moyen de réutiliser le code commun et d'implémenter une autre méthode qui peut nécessiter un appel pour appeler l'appel système sous-jacent, mais qui n'a pas abouti car les appels read et recv sont différents.éliminer le code en double pour des définitions de fonctions similaires

Serait formidable de trouver une solution élégante à peu près la même chose. Les méthodes ressemblent -


Première fonction

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

deuxième fonction

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

Pourriez-vous expliquer ce que le code est censé faire? Je comprends l'essentiel, mais je pense qu'il y a probablement des façons plus simples et plus rapides de mettre en œuvre les deux. – tambre

+0

Bien que je devrais mentionner que le moyen facile serait de simplement diviser la logique de fonction en différentes fonctions. Rien de bouleversant. – tambre

+0

@tambre lit à partir de ' socket' et retourne un' std :: string'. –

Répondre

2

En C++ 14 vous pouvez le faire de cette façon. C'est une solution plus flexible, bien que cela ne soit pas conçu pour répondre exactement à vos besoins. Et par conséquent, il peut s'avérer être moins expressif.

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

Vous pouvez déclarer des variables locales dans foo et bar, et DucplicatedCode les transmettra à fn ou vous pouvez simplement capturer ces variables.

+0

Ceci est très bonne méthode imho, est-ce aussi l'exemple parfait pour le transfert, non? Pourriez-vous vérifier - http://ideone.com/WNm35N, si c'est l'utilisation prévue de votre solution. –

+0

Oui, exactement. Lambda expression n'est pas nécessaire ici, vous obtenez ce point, vous obtenez l'idée:). Vous pouvez également modifier l'appel à fn si nécessaire (passer une variable locale dans DuplicatedCode etc.) – felix

3

Quand je vais comparer vos versions de std::string Socket::read(const int bufSize) const et std::string Socket::recv(const int bufSize, SF::recv flags) const

enter image description here la seule différence est

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

et

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

Ainsi votre 1ère version pourrait être remaniée à un appel de la 2ème version en utilisant un ensemble spécifique de flags.

Ou vous pourriez fournir une valeur par défaut pour flags comme

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

La solution la plus simple est de résumer les méthodes et passer un indicateur non valide, si le message de lecture est appelé:

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

MAIS, cela violerait la Single Responsibility Principle, parce que la méthode a maintenant deux responsabilités et deux raisons changer.

Une meilleure solution sera d'extraire les parties communes des deux méthodes. Cependant, votre question est peut-être fondée sur des opinions différentes, mais celui-ci est à moi.;-)

1

Une façon simple est d'ajouter une fonction d'assistance privée au sein de la classe 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 
};  

et mettre en œuvre comme

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  
} 

Ensuite, tout ce que vous devez faire est réimplémentez vos deux fonctions appeler l'aide.

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

Notez que j'ai supprimé les mots const redondants d'arguments passés par valeur.