Getting Rid of "char*"

Started by
38 comments, last by Brain 9 years, 4 months ago

So, I would like to stop using char* and char[ size ] to allocate and pass around strings. Instead I want to use a string class.

The string class allocates some memory on the stack (like 25 characters worth or so) to optimize the use of small strings. I have written a class that works the same, however, it also allows you to set the amount of 'stack allocated memory' using a template parameter. Like so:

MyString< SIZE > string;

This allows it to function just as if you were pre-allocating a char array like so:

char string[ SIZE ];

However, when I return a char array I just use a char*, and I don't need to know how much memory was allocated for that buffer. With MyString, when I return it I need to specify exactly how much memory was allocated for it, and it's a pain in the ass! Something like this:

const MyString< SIZE >& GetFaceName( );

Which is ridiculous, since I always have to know its exact size. How can I wrap char arrays into a class and make it not suck so bad? The only thing I can think of is to maybe have MyString inherit an interface IMyString and return that. But I'm not sure if that's the best way to solve this problem.

ALSO: Hope your Christmas was good!

Advertisement
Before we delve deeper is there any reason why you seem to have fallen into the common trap (possibly form of antipattern) of reimplementing std::string?

Part of the reason I reimplemented std::string is because I wanted to have a string class that I could use as a replacement for preallocated character arrays, with the same performance characteristics and locality of memory.

To do that I require a string which allocates memory to the stack, and to specify the amount I want I need to pass it as a template parameter.

EDIT: Also, reimplementing std::string has little extra code due to it mostly calling into the methods of my reimplemented std::vector tongue.png

Use an abstract base class:


class MyStringBase {
   public:
   virtual const char* GetAsChar () const = 0;
   virtual void SomeOtherFunction () = 0;
   };

template <uint32_t Size> class MyString : MyStringBase {
   public:
   virtual const char* GetAsChar () const { return data; }
   // ect...
   };

Or rework your functions to take a template parameter:


template <typename T> void PrintString (T&& s) {
   s.PrintOrSomeFunction();
   }

template <uint32_t S> void PringString2 (const MyString<S>& s) {
   s.PrintMoreStuff();
   }

Part of the reason I reimplemented std::string is because I wanted to have a string class that I could use as a replacement for preallocated character arrays, with the same performance characteristics and locality of memory.

To do that I require a string which allocates memory to the stack, and to specify the amount I want I need to pass it as a template parameter.

EDIT: Also, reimplementing std::string has little extra code due to it mostly calling into the methods of my reimplemented std::vector tongue.png

So do you actually have a reason for reimplementing standard library classes or do you just assume they're more performant or something?

Very rarely(as in, AAA production) do I see someone talk about replacing something in the standard library and not actually making their code -worse-.

Part of the reason I reimplemented std::string is because I wanted to have a string class that I could use as a replacement for preallocated character arrays, with the same performance characteristics and locality of memory. To do that I require a string which allocates memory to the stack, and to specify the amount I want I need to pass it as a template parameter. EDIT: Also, reimplementing std::string has little extra code due to it mostly calling into the methods of my reimplemented std::vector :P

So do you actually have a reason for reimplementing standard library classes or do you just assume they're more performant or something?Very rarely(as in, AAA production) do I see someone talk about replacing something in the standard library and not actually making their code -worse-.

I find that rarely in any professional development role do you reimplement standard library features at all. This is known as NIH (not invented here) syndrome, and costs time and money in terms of testing and development. Very rarely is a reimplementation of standard library useful unless you are working on embedded systems or low level operating system code (e.g. Your own standard C library)

I agree that replacing stl functions isn't necessary and will cost money and time, however, discussing when and how to do so is outside the scope of this thread.

As for the solution to my issue, it seems like the only way to do it is to use interfaces, as Ryan_001 suggested. This is because using template methods will not work with interfaces, and my interfaces need to be able to return strings.

Sadly, this precludes the use of template methods inside the MyString class as well, which thankfully in this case only adds a bit of extra header code.

Like Ryan_001 said, you'll either need to provide an interface for your family of String classes and program to that interface, or provide a family of function overloads for each member of your String class. I'd recommend the interface method, since you can't overload on a return type so you'll need to explicitly instantiate such templated function overloads at the call point instead of relying on deduction, and that can quickly make you code messier.

Don't worry about all the bloat that using a separate class for every possible string length will introduce. Modern computers have more than enough memory and disk space to handle that and statistically speaking you're unlikely to have more than a few hundred such classes (or function overloads). If it turns out all the code fragmentation blows your cache coherency noticably, get a bigger i7 with more cache.

Stephen M. Webb
Professional Free Software Developer

I think I detect a bit of sarcasm in the second paragraph, and I note the issue you point out. Thankfully, I only need roughly 3 different sizes throughout my entire application, and the instruction cache can more than handle that.

Having said that, I do wonder if there is a better way. In the end, I simply want a string which allocates on the stack like a char[ Size ] array. Funnily enough, I'm not sure C++ is up to the task without major drawbacks one way or another (for example instruction cache bloat if overused). Maybe that's why everyone just passes around char*.

Use std string for strings. Use std vector<char> for mutable char arrays. Conversion between the two is trivial via iterator constructors.

These are distinct use cases and trying to crowbar std string to represent a mutable array of char is probably your problem.

Whether or not small strings are on the heap or the stack is probably not an issue. Your runtime manages memory better than one might naively think.

This topic is closed to new replies.

Advertisement