tags:

views:

1829

answers:

15

I've not used C++ very much in the past, and have recently been doing a lot of C#, and I'm really struggling to get back into the basics of C++ again. This is particularly tricky as work mandates that none of the most handy C++ constructs can be used, so all strings must be char *'s, and there is no provision for STL lists.

What I'm currently trying to do is to create a list of strings, something which would take me no time at all using STL or in C#. Basically I want to have a function such as:

char **registeredNames = new char*[numberOfNames];

Then,

RegisterName(const * char const name, const int length)
{
    //loop to see if name already registered snipped
    if(notFound)
    {
        registeredNames[lastIndex++] = name;
    }

}

or, if it was C#...

if(!registeredNames.Contains(name))
{
    registeredNames.Add(name);
}

and I realize that it doesn't work. I know the const nature of the passed variables (a const pointer and a const string) makes it rather difficult, but my basic problem is that I've always avoided this situation in the past by using STL lists etc. so I've never had to work around it!

I'm sorry for posting such a trivial question, and any help will be hugely appreciated!

Cheers,

xan

+1  A: 

Edit: I guess I misunderstood your question. There is no constness problem in this code I'm aware of.

I'm doing this from my head but it should be about right:

static int lastIndex = 0;
static char **registeredNames = new char*[numberOfNames];

void RegisterName(const * char const name)
{
    bool found = false;
    //loop to see if name already registered snipped
    for (int i = 0; i < lastIndex; i++)
    {
        if (strcmp(name, registeredNames[i] == 0))
        {
            found = true;
            break;
        }
    }

    if (!found)
    {
        registeredNames[lastIndex++] = name;
    }
}
Maximilian
this would not compile bool found is inside for and the scope is inside for. you have to move bool found = false; prior to for.
smink
Thanks, I corrected it.
Maximilian
+4  A: 

You'll probably need to use strcmp to see if the string is already stored:

for (int index=0; index<=lastIndex; index++)
{
  if (strcmp(registeredNames[index], name) == 0)
  {
    return; // Already registered
  }
}

Then if you really need to store a copy of the string, then you'll need to allocate a buffer and copy the characters over.

char* nameCopy = malloc(length+1);
strcpy(nameCopy, name);
registeredNames[lastIndex++] = nameCopy;

You didn't mention whether your input is NULL terminated - if not, then extra care is needed, and strcmp/strcpy won't be suitable.

Seb Rose
+1  A: 

Working with char* requires you to work with C functions. In your case, what you really need is to copy the strings around. To help you, you have the strndup function. Then you'll have to write something like:

void RegisterName(const char* name)
{
  // loop to see if name already registered snipped
  if(notFound)
  {
    registerNames[lastIndex++] = stdndup(name, MAX_STRING_LENGTH);
  }
}

This code suppose your array is big enough.

Of course, the very best would be to properly implement your own string and array and list, ... or to convince your boss the STL is not evil anymore !

PierreBdR
+1  A: 

Using:

const char **registeredNames = new const char * [numberOfNames];

will allow you to assign a "const * char const" to an element of the array.

Just out of curiosity, why does "work mandates that none of the most handy C++ constructs can be used"?

Skizz

Skizz
Because I work for a games company and the code has to be super-portable. I havn't been here that long, but as far as I can make out, STL and most other libraries not provided by the toolkit we use is "out of bounds"
xan
I don't think that are any platforms where the STL isn't available anymore, but at the very least I would suggest to your boss that anyone of you company sits down and writes a string/collection library for all of you to use.Diddling with shit like this is just... unproductive.
Maximilian
I would rather tend to agree with you *sigh*. I'm going to have to poke my supervisor and get some *apologies for the pun* pointers...
xan
Yeah, avoiding the STL will cut your development productivity by 50%. Get permission to use the STL (and boost smart pointers!) even if you need to buy an STL library for some platforms. To sacrifice that kind of developer productivity without a careful analysis of STL portability is expensive.
emk
Games companies are usually run by people who haven't got a clue or must invent everything here (I have been a games programmer). STL stands for "*Standard* Template Library". It's a standard. And it's portable, especially things like STLPort (http://www.stlport.org/product.html).
Skizz
+3  A: 

Why can't you use the STL?

Anyway, I would suggest that you implement a simple string class and list templates of your own. That way you can use the same techniques as you normally would and keep the pointer and memory management confined to those classes. If you mimic the STL, it would be even better.

Ferruccio
Because I work for a games company and the code has to be super-portable. I havn't been here that long, but as far as I can make out, STL and most other libraries not provided by the toolkit we use is "out of bounds"
xan
Hi xan,I've written game-code as well, and I didn't had any problems running STL code on PS2 or XBox. I even tried it on the good old PsOne and it worked (code bloated a bit though).
Nils Pipenbrinck
Hi Nils. Ever targetted Symbian? STLPort does work for it, but a company isn't going to agree to throw it into their game just for one programmer who wants a vector of strings.
Steve Jessop
I don't understand why. The nice thing about template libraries is that you only pay for what you use, so adding STLport and just using strings and lists will only add as much as writing those functions yourself.
Ferruccio
+5  A: 

If portability is an issue, you may want to check out STLport.

Ferruccio
+1  A: 

If you really can't use stl (and I regret believing that was true when I was in the games industry) then can you not create your own string class? The most basic of string class would allocate memory on construction and assignment, and handle the delete in the destructor. Later you could add further functionality as you need it. Totally portable, and very easy to write and unit test.

David Sykes
+1  A: 

I can understand why you can't use STL - most do bloat your code terribly. However there are implementations for games programmers by games programmers - RDESTL is one such library.

graham.reeds
A: 

Are you working on some extremely limited platform with no standard library, or are your managers just idiots?

DrPizza
+4  A: 

I'll probably get voted down for this answer, because it's not really an answer, but there are legitmate reasons that STL might be avoided. When working in fixed environemnts where memory or speed is a premium, it's sometimes difficult to tell what is going on under the hood with STL. Yes, you can write your own memory allocators, and yes, speed generally isn't a problem, but there are differences between STL implementations across platforms, and those differences mighe be subtle and potentially buggy. Memory is perhaps my biggest concern when thinking about using it. I too work for a game company, and have for a long time. Memory is precious, and how we use it needs to be tighly controlled. Unless you've been down this road, this concept might not make sense, but it's true. We do allow for STL usage in tools (outside of game code)l, but it's prohibited inside of the actual game. One other related problem is code size. I am slightly unsure of how much STL can contribute to executable size, but we've seen marked increases in code size when using STL. Even if your executable is "only" 2M bigger, that's 2M less RAM for something else for your game.

STL is nice for sure. But it can be abused by programmers who don't know what they are doing. It's not intentional, but it can provide nasty surprises when you don't want to see them (again, memory bloat and performance issues)

I'm sure that you are close with your solution.

for ( i = 0; i < lastIndex; i++ ) {
    if ( !strcmp(&registeredNames[i], name ) {
        break;    // name was found
    }
}
if ( i == lastIndex ) {
    // name was not found in the registeredNames list
    registeredNames[lastIndex++] = strdup(name);
}

You might not want to use strdup. That's simply an example of how to to store the name given your example. You might want to make sure that you either don't want to allocate space for the new name yourself, or use some other memory construct that might already be available in your app.

And please, don't write a string class. I have held up string classes as perhaps the worst example of how not to re-engineer a basic C construct in C++. Yes, the string class can hide lots of nifty details from you, but it's memory usage patterns are terrible, and those don't fit well into a console (i.e. ps3 or 360, etc) environment. About 8 years ago we did the same time. 200000+ memory allocations before we hit the main menu. Memory was terribly fragmented and we couldn't get the rest of the game to fit in the fixed environment. We wound up ripping it out.

Class design is great for some things, but this isn't one of them. This is an opinion, but it's based on real world experience.

Mark
There's nothing wrong with STL and limited devices. STL uses allocators which can be user defined. Create an allocator that only allocates from a fixed amount of RAM and hey presto no memory fragmentation in the rest of the system.
Skizz
sure, assuming that you can provide the proper amount of RAM. All this stuff can get fixed over time, and has. The fact is you must understand your allocation patterns to make sure that you don't run out of RAM. Most people don't understand this and they wind up with problems.
Mark
And you still might wind up with fragmentation from your personal heap that you've created and are allocating from. Creating an allocator doesn't solve that problem, it only localizes it.
Mark
So what did you replace it with? If there were 200000+ allocations going on, it seems to me to be a problem with the system and not just the class, although a poorly implemented class can be innefficent (see Scott Meyers Effective C++ books).
Skizz
we replaced it with fixed sized C style strings and standard C string calls. We knew enough about the sizes of strings that we needed that we just fixed their length into things like MAX_STRING_LEN
Mark
A: 

If you are not worried about conventions and just want to get the job done use realloc. I do this sort of thing for lists all of the time, it goes something like this:

T** list = 0; unsigned int length = 0;

T* AddItem(T Item) { list = realloc(list, sizeof(T)*(length+1)); if(!list) return 0; list[length] = new T(Item); ++length; return list[length]; }

void CleanupList() { for(unsigned int i = 0; i < length; ++i) { delete item[i]; } free(list) }

There is more you can do, e.g. only realloc each time the list size doubles, functions for removing items from list by index or by checking equality, make a template class for handling lists etc... (I have one I wrote ages ago and always use myself... but sadly I am at work and can't just copy-paste it here). To be perfectly honest though, this will probably not outperform the STL equivalent, although it may equal its performance if you do a ton of work or have an especially poor implementation of STL.

Annoyingly C++ is without an operator renew/resize to replace realloc, which would be very useful.

Oh, and apologies if my code is error ridden, I just pulled it out from memory.

jheriko
A: 

All the approaches suggested are valid, my point is if the way C# does it is appealing replicate it, create your own classes/interfaces to present the same abstraction, i.e. a simple linked list class with methods Contains and Add, using the sample code provided by other answers this should be relatively simple.

One of the great things about C++ is generally you can make it look and act the way you want, if another language has a great implementation of something you can usually reproduce it.

titanae
A: 

const correctness is still const correctness regardless of whether you use the STL or not. I believe what you are looking for is to make registeredNames a "const char **" so that the assignment to registeredNames[i] (which is a const char *) works.

Moreover, is this really what you want to be doing? It seems like making a copy of the string is probably more appropriate.

Moreover still, you shouldn't be thinking about storing this in a list given the operation you are doing on it, a set would be better.

Greg Rogers
A: 

I have used this String class for years.

http://www.robertnz.net/string.htm

It provides practically all the features of the STL string but is implemented as a true class not a template and does not use STL.

Roger Nelson
A: 

This is a clear case of you get to roll your own. And do the same for a vector class.

  • Do it with test-first programming.
  • Keep it simple.

Avoid reference counting the string buffer if you are in MT environment.

James Dean