4

In one of our class sir said that template allows one to customize behavior of class, and then he gave example of string class, that with few lines of code we can customize string class from STL, as in, we can make it to treat 'a' and 'z' same, 'b' and 'y' same, 'c' and 'x' same and so on. Similary 'A' and 'Z' same etc.

"abc" == "zyx" is true;
"Abc" == "zyx" is false;
"Abc == "Zyx" is true;

etc

I was thinking of implementing such string class, but I am not able to do so. How can we implement such string class using templates?

The Unfun Cat
  • 29,987
  • 31
  • 114
  • 156

3 Answers3

3

It's very tricky. All you need to write your own traits class, specifically you need to derive it from char_traits<> class template, and redefine eq() and compare() function (Note: only redefining eq() would not work; even though there is no change in the redefinition of compare(), you've to write it in your derived class as such!). Lets say this traits class sequence_traits and call your custom string sequence. After all, string is a sequence of characters!

Note : What I understand from your post that you want alphabets[i] == alphabets[25-i] to be treated as same, that means, first letter and last letter same, second letter and second last letter same, and so on!

struct sequence_traits : char_traits<char>
{
    //'a' and 'z' are equal
    //'b' and 'y' are equal
    //'c' and 'x' are equal, and so on.
    //that implies, 'a' + 'z' == 'b' + 'y' == 'c' + 'x' == 'd'  + 'w == so on
    //same for upper cases!
    static bool eq(const char& left, const char& right)
    {   
        return ( left == right) || (left + right == 'a' + 'z') || ( left + right == 'A' + 'Z') ;
    }
    static int compare(const char *first1, const char *first2, size_t count)
    {   
        for (; 0 < count; --count, ++first1, ++first2)
            if (!eq(*first1, *first2))
                return (lt(*first1, *first2) ? -1 : +1);
        return (0);
    }
};

And then you can do this typedef for easy use:

typedef basic_string<char, sequence_traits> sequence;

You're done. You can use sequence now. :-)

Working example : http://www.ideone.com/ByBRV


Read this article to know how it works in detail : http://www.gotw.ca/gotw/029.htm

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • I'm not sure about C++, but in C, identifiers that begin with an underscore and an uppercase letter (i.e. `_Left` and `_Right`) are reserved and using them result in undefined behaviour. – dreamlax Jan 04 '11 at 04:03
  • 2
    @Nawaz: In the C++ standard, §17.4.3.1.2 defines reserved identifiers regarding underscores, and §17.4.3.1 paragraph 3 defines the behaviour of using reserved identifiers. – dreamlax Jan 04 '11 at 04:12
  • @dreamlax : read this carefully what $17.4.3.1/3 says, "If the program declares or defines a name **in a context where it is reserved**, other than as explicitly allowed by this clause, the behavior is undefined.".... so I didn't use underscores "in a context where it is reserved". It's local to the functions! – Nawaz Jan 04 '11 at 04:21
  • @dreamlax : Anyway, I made my variables simples. Actually I copy-pasted the functions from the base class `char_traits' and made appropriate changes without changing the variables name though! – Nawaz Jan 04 '11 at 04:30
  • 1
    But §17.4.3.1.2 says "Certain sets of names and function signatures are **always reserved** to the implementation: — Each name that contains a double underscore `_ _` **or begins with an underscore followed by an uppercase letter** (2.11) is reserved to the implementation **for any use**." This to me implies that there is no suitable context to use names beginning with an underscore and capital letter. This is different to the second point of §17.4.3.1.2 which specifically deals with the global namespace. – dreamlax Jan 04 '11 at 04:33
  • @Nawaz: The *implementation* can use those names because the standard permits it (the part that says *reserved to the implementation for any use*). – dreamlax Jan 04 '11 at 04:37
  • I tested this with many strings. this is working. but I didn't understand how it works. Can you explain? –  Jan 04 '11 at 04:41
  • @truman : read the article I provided the link of. That explains very well. Besides, you also need to look into the basic_string<> class template. – Nawaz Jan 04 '11 at 04:43
  • @dreamlax : then please explain "in a context where it is reserved" used in $17.4.3.1/3. :-) – Nawaz Jan 04 '11 at 04:44
  • @Nawaz: Consider the second part of §17.4.3.1.2, where it explicitly states that the restriction only applies to the global namespace. Clearly in this instance, the context in which the names are reserved is the global namespace. – dreamlax Jan 04 '11 at 04:48
  • @dreamlax : so you think inside the function where the variables are visible is "global" namespace? – Nawaz Jan 04 '11 at 05:44
  • @Nawaz: The second part is not the part I was talking about, I mentioned it because it had explicitly stated the context in which it applied to. Read carefully the first quote that says specifically names beginning with an underscore followed by an uppercase letter are always reserved for any use (implying that the restriction applies to all contexts from the words "always reserved" and "for any use"). – dreamlax Jan 04 '11 at 06:32
  • @dreamlax : no, that doesn't mean that. You need to understand all parts together; not just one part, while overlooking others! – Nawaz Jan 04 '11 at 07:01
  • @Nawaz: The standard defines the behaviour for using reserved names in a context where they are reserved (§17.4.3.1), and it clearly says names beginning with two underscores or an underscore and an uppercase letter are **always reserved** (all contexts) and that names beginning with an underscore are reserved in the global namespace (the context is the global namespace). – dreamlax Jan 04 '11 at 07:37
  • dreamlax : "always reserved" doesn't mean "in all context". It means, in some contexts where they're "always reserved". – Nawaz Jan 04 '11 at 08:40
  • @Nawaz: Actually, it does. See [this answer](http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier) for more information. – dreamlax Jan 19 '11 at 20:55
2

You'll want to look at the char_traits type. This is an example of one type of traits class that you can use in with basic_string in order to get a working string type. If you define your own traits class, you can build a custom string like this:

class CustomTraits { ... };
typedef basic_string<char, CustomTraits> CustomString;

And now the traits defined by CustomTraits will be used to determine how the string works.

As an example along the lines of what you were saying, you could do something like this:

class CustomTraits: public char_traits<char> {
public:
    /* Redefine equality to compare 'a' and 'z' equal. */
    static bool eq(char one, char two) {
         return one == two || (one == 'a' && two == 'z' || one == 'z' && two == 'a');
    }
};
typedef basic_string<char, CustomTraits> StringWithAAndZEqual;

Now, you can use the new type and the class will treat 'a' and 'z' identically.

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • @templatetyepdef : I think, this is NOT what he wants. this treats only 'a' and 'x' equivalently, but not all other pairs, like 'b' and 'y', 'c' and 'x'. I think what he wants is `alphabets[i] == alphabets[25-i]` should be treated as same, that means, first letter and last letter same, second letter and second last letter same, and so on! – Nawaz Jan 04 '11 at 04:24
  • 1
    this is not working. it works for 'a' and 'z', not 'b' and 'y' etc pairs. –  Jan 04 '11 at 04:42
  • 1
    My apologies - I misread the question. This was mostly a proof-of-concept, though; there are a few other things missing as well (such as lt, compare, etc.) – templatetypedef Jan 04 '11 at 04:50
2

You want to make your custom char_traits and instantiate std::basic_string with that.

wilhelmtell
  • 57,473
  • 20
  • 96
  • 131
  • Can you still compare two std::basic_string if they have different `char_traits` with the `==` operator? – dreamlax Jan 04 '11 at 03:58
  • 1
    Not naively. If `my_string` is an instance of your custom string then you'll need to do something like `my_string == a_std_string.c_str()`. The same holds for using your string with streams: you'll probably need to use `std::basic_string<>::c_str()`. – wilhelmtell Jan 04 '11 at 04:28