Is it possible that std :: string always contains a string string?

Is it possible that std :: string always contains a string string? this is how i will use it:

typedef std::basic_string<...> lowercase_string; void myfunc() { lowercase_string s = "Hello World"; // notice mixed case printf(s.c_str()); // prints "hello world" in lowercase std::string s2 = s; printf(s2.c_str()); // prints "hello world" in lowercase } 
+6
source share
4 answers

You can write your own char features and pass it to std::basic_string as the second argument to the template.

Here is a minimal example:

 template<typename T> struct lowercase_char_traits : std::char_traits<T> { static T* copy(T* dest, const T* src, std::size_t count ) { for(size_t i = 0 ; i < count ; ++i) dest[i] = std::tolower(src[i]); return dest; } static void assign(T & out, T in) { out = std::tolower(in); } //implement other overload of assign yourself //note that you may have to implement other functionality //depending on your requirement }; 

And then define typedef as:

 typedef std::basic_string<char, lowercase_char_traits<char>> lowercase; 

And here is the test program:

 int main() { lowercase s1 = "Hello World"; std::cout << s1.c_str() << std::endl; lowercase s2 = "HELLO WORLD"; std::cout << std::boolalpha << (s1 == s2) << std::endl; lowercase s3 = "HELLO"; s3 += " WorL"; s3.append("D"); std::cout << std::boolalpha << (s1 == s3) << std::endl; std::cout << s2.c_str() << std::endl; std::cout << s3.c_str() << std::endl; } 

Conclusion:

 hello world true true hello world hello world 

Cool, right?


Note that for a full-fledged string string string class, you may also need to define other lowercase_char_traits functions, depending on what kind of behavior you want from such a class.

Take a look at Herb Sutter's brilliant article for details and explanations:

Hope this helps.

+18
source

std::string itself does not.

There are various alternatives, more or less elegant and more or less pros and cons. Let me try to compare them.

encapsulation completion

Perhaps the cleanest solution is to create a class containing std :: string, and which can accept commands and assignments that perform the conversion.

  • problem: std :: sting has over a hundred methods: if you want your class to expose all of them ... be prepared to write all the hose functions just to call wrapped ones. This is a pure "performance" problem, which is not, is not, apparently, OOP cares ... maybe they are paid for with a typed character ... :-)
  • advantage: no run-time polymorphism (not supported by std :: string) can accidentally work, so the code is more secure.

"partial" packaging

Same as before, but applies only to some important methods or requires some explicit coding.

A typical implementation may be:

may be:

 class llstring { public: //just esplicitate a default llstring() :m() {} //this wors for all the std::string contructors but the ones specifically defined here template<class T, class... TT> llstring(T&& t, TT&&... tt) :m(std::forward<T>(t), std::forward<TT>(tt)...) {} // copy and move defaulted: just call the memebr ones llstring(const llstring&)=default; llstring(llstring&&) =default; //impose conversion llstring(const std::string& s) :m(lowercase(s)) {} llstring(const char* s) :m(lowercase(s)) {} //assign and transfer defaulted llstring& operator=(const llstring&)=default; llstring& operator=(llstring&&)=default; //impose conversion llstring& operator=(const std::sting& s) { m = lowercase(s); return *this; } llstring& operator=(const char* s) { m = lowercase(s); return *this; } //gets the "value" const std::string& str() const { return m; } private: std::string m; }; 

This class itself is incapable of any algorithm and operation, but can participate in any type of std::string by calling str() . And it can accept any std :: string result, getting the conversion.

Probably a good compromise between transcoding and maintenance risks

Hereditary

std :: string as a base, not a member. The code is similar to the above (you must provide a conversion method when building or assigning)

  • advantages: the interface and behavior of orioginal std :: string are automatically displayed, therefore all methods of std :: string work and are available.

  • neutral: both conversion forward (by design) and backward (by basic inheritance) from std :: string work. This can lead to some ambiguity with some operations that may not go through llstring. This is not a problem in itself, but you must be sure how the name resolution and name binding will be performed. The jaguar is well indicated, but it is one of the sides of the language that is not always known to any average programmer.

  • disavantage: llstring exposes a polymorphic behavior towards std :: string, which does not behave politically to llstring (no methods are virtual, including a destructor), so you should never call delete on std :: string * (this is Undefined Behavior, if it points to "llstring").

Given that both llstring and string are value types, this usually should not happen (for 30 years I have never written a single new std::string or delete pstring ). But this in any case will catch all the tirades of the OOP fanatist, claiming the classical OOP rules, to apply to strings, even if they are not an object of OOP.

But there is another -IMHO more subtle risk: in the composite expression between llstring and the string, all intermediate results will be string. And the intermediate operation will not convert between them. And all this is implicit. Again, the language specification is well defined, but it can be tricky to take control of everything. A search for an intermediate result that has not yet been assigned may fail ... due to an unexpected capital letter inside.

Inverse transformation

Not exactly what you asked for, but ... maybe it's better to deal with the problem.

Instead of "convert when replacing the destination", "convert when leaving the source":

write a wrapper (for example, in the section "partial deformation", above), instead of implicitly converting a string from and having an explicit function str (), it takes an explicit construct from n strings (even without conversion) and has an implicit string conversion to ( operator std::string() { return lowercase(m); } )

It works the opposite, as you requested. It’s good if the number of points at which the existence of header lines is allowed has little respect for the common lines in your program (which you can always consider to be lowercase), and if you can provide that all the std :: string operations you can implement are string values case will never be generated in uppercase.

Edit: char_traits solution

Added after Nawaz post :

The solution will try to change the behavior. (not value ) by making char stick to different semantics.

  • Advantage: Simple and does not require large wrappers. Fast coding.
  • Disavantage: may not be exactly what it was intended: since the std :: string functions are available, and since the copy may not be the only way to change the string contents, you are not provided (in any case) that the characters in it will never be capitalized. If you cannot provide this copy, this is the only way to change the string value.

Note: just like string , char_traits does not have a virtual destructor, but, unlike a string, no OOP resident usually shouts about inheritance from it. And if they ask him, they will most likely say: "There will be no dynamic allocation to char_traits." Goodbye coherence.

Finally

There is no "perfect solution" with a "low cost." All of them are somehow ineffective at some stage.

+2
source

You can use private inheritance. This frees you from writing a bunch of wrapper methods.

 class lower_case_string : private std::string { // define constructors that do the conversion to lower case // ... // expose functionality from std::string using std::string::size; using std::string::length; using std::string::cbegin; // etc. // Make sure NOT to expose methods that allow modification as they // could violate your invariant that all characters are lower case. // Eg, don't expose std::string::begin, instead write your own. }; 
+2
source

You will need to create a wrapper (as mentioned by Chad in the comments, it is better to use composition for this) around a class that converts each character to its lower version in its constructor. Unfortunately, std::string cannot do this out of the box. Alternatively, create your own functions to print the string, or convert the string to lowercase before printing it.

0
source

All Articles