2014-11-01 17 views
6

Nel mio progetto ci sono un sacco di stringhe con significati diversi, allo stesso scopo, come:Come implementare le stringhe digitate in C++ 11?

std::string function_name = "name"; 
std::string hash = "0x123456"; 
std::string flag = "--configure"; 

voglio distinguere i diversi stringhe dal loro significato, da usare con sovraccarichi di funzioni:

void Process(const std::string& string_type1); 
void Process(const std::string& string_type2); 

Ovviamente, devo utilizzare diversi tipi:

void Process(const StringType1& string); 
void Process(const StringType2& string); 

Ma come attuare questi tipi in modo elegante? Tutto quello che posso venire è questo:

class StringType1 { 
    std::string str_; 
public: 
    explicit StringType1(const std::string& str) : str_(str) {} 
    std::string& toString() { return str_; } 
}; 

// Same thing with StringType2, etc. 

Potete consigliare un modo più conveniente?


v'è alcun punto nelle funzioni di ridenominazione in quanto l'obiettivo principale è quello di non passare per errore un tipo stringa invece di un altro:

void ProcessType1(const std::string str); 
void ProcessType2(const std::string str); 

std::string str1, str2, str3; 

// What should I pass where?.. 
+0

_ "distinguere diverse stringhe per il loro significato" _ Potrebbe illustrare _their meaning_ per favore? Puzza un po 'di problema XY per me. –

+0

Sì, è chiaramente un problema XY. È eccessivo avere proprie classi che incapsulano i dati in questo caso. Dai un'occhiata ai modelli di strategia. –

+0

@ πάνταῥεῖ solo per dare un'occhiata - non è molto utile. Potrebbe essere possibile espandere l'utilizzo del modello suggerito a una risposta? –

risposta

1

Il design si sono intesi per l'eredità, come nel other answer here (*). Ma non dovresti ereditare da std :: string. Puoi trovare molte discussioni a riguardo, ad esempio: Inheriting and overriding functions of a std::string?.

Ti lascia con la tua prima idea, implementando effettivamente il concetto di composizione.

(*) Avrei commentato quella risposta invece di aprire una nuova risposta, ma non posso ancora commentare.

6

probabilmente avrete bisogno di un modello con un parametro tag:

template<class Tag> 
struct MyString 
{ 
    std::string data; 
}; 

struct FunctionName; 
MyString<FunctionName> function_name; 
+0

In questo caso non penso sia opportuno utilizzare l'overload di funzioni, ma +1 per il tag - – ikh

2

approccio semplice:

struct flag { 
    string value; 
}; 
struct name { 
    string value; 
}; 

si potrebbe migliorare questo con conversioni implicite alle stringhe o altri memberfunctions, anche.

0

Questo suona un po 'basso ackwards. Vuoi avere lo stesso nome per più funzioni che fanno cose diverse per oggetti diversi. È piuttosto strano, perché l'approccio tipico è quello di avere lo stesso nome per funzioni che fanno cose diverse.

Non passare "le cose sbagliate" dipende in gran parte dal programmatore, dovresti ricordare cosa stai facendo, e cosa. E per testare il tuo codice mentre lo scrivi, e fare test che si eseguono regolarmente per evitare regressioni.

L'altro approccio è quello di avere un insieme di classi che contengono i dati, e hanno un'interfaccia comune, come ad esempio questo:

class OptionBase 
{ 
public: 
    OptionBase(const std::string &s) : str(s) {} 
    virtual void Process() = 0; 
    virtual std::string Value() { return str; } 
    virtual ~OptionBase() {} 
protected: 
    std::string str; 
}; 

class FlagOption: public OptionBase 
{ 
public: 
    FlagOption(const std::string& s) : OptionBase(s) {} 
    void Process() override { ... do stuff here ... } 
}; 

class HashOption: public OptionBase 
{ 
public: 
    HashOption(const std::string& s) : OptionBase(s) {} 
    void Process() override { ... do has stuff here ... } 
}; 


class FunctionName: public OptoonBase 
{ 
    ... you get the idea ... 
}; 

Ora è possibile "processo" tutte le tue OptionBase cose tipo in modo coerente modo, chiamando la stessa funzione di elaborazione su ciascuno di essi. Ma non sono sicuro che sia quello che stavi cercando.

2

È possibile utilizzare una tecnica simile a quella applicata a std::vector in this answer.

Ecco come sarebbe simile per std::string:

#include <iostream> 
#include <string> 

template<typename Tag, class T> 
struct allocator_wrapper : T 
{ using T::T; }; 

template< typename Tag, 
      typename CharT = char, 
      typename Traits = std::char_traits<CharT>, 
      typename Allocator = std::allocator<CharT> > 
using MyString = std::basic_string<CharT,Traits,allocator_wrapper<Tag, Allocator>>; 

class HashTag; 
class FlagsTag; 

using Hash = MyString<HashTag>; 
using Flags = MyString<FlagsTag>; 

void foo(Hash) {} 

int main() 
{ 
    Hash hash("12345"); 
    Flags flags("--foo"); 

    foo(hash); // that's fine 

    // you can already use them like strings in *some* contexts 
    std::cout << hash << " - " << flags << std::endl; 
    std::cout << (hash == "12345") << std::endl; 

    // but they are not compatible types: 
    // won't compile: 
    // foo(flags); 
    // std::cout << (hash == flags) << std::endl; 
} 
Problemi correlati