15

I recently became aware that the strdup() function I've enjoyed using so much on OS X is not part of ANSI C, but part of POSIX. I don't want to rewrite all my code, so I think I'm just going to write my own strdup() function. It's not that hard, really, it's just a malloc() and a strcpy(). Anyway, I have the function, but what am I doing if I write this function and link it to my code, and it already exists in the libc? Will my linker or compiler allow me to basically define my own version of the function, or do I have to give it another name? It would be terribly convenient if there was a way to reuse the same name, so that if strcpy() exists in the user's libc they could use that, but if it didn't exist in their libc they could use my version instead, with as little code change as possible.

The short version:

a) What happens when I write my own function with the same name as a built-in function?

b) What can I do to avoid bad things happening to me on platforms that don't have strdup() without rewriting all my code to not use strdup(), which is just a bit tedious?

apaderno
  • 28,547
  • 16
  • 75
  • 90
Chris Lutz
  • 657
  • 2
  • 6
  • 8

7 Answers7

20

Usually, you just use an #if to define the function you want when under a certain compiler. If the built-in library doesn't define strdup, there is no problem in defining it yourself (other than if they do define it in the future, you'll have to take it out.)

// Only define strdup for platforms that are missing it..
#if COMPILER_XYZ || COMPILER_ABC
char *strdup(const char *)
{
   // ....
}
#endif
Eclipse
  • 44,851
  • 20
  • 112
  • 171
  • 8
    If you're feeling ambitious, you could even set up automake and autoconf to configure the macro necessary to test whether you need to define strdup yourself, instead of enumerating all the compilers and environments you wish to support. – Rob Kennedy Jan 27 '09 at 06:28
6

you could just use a macro like this, this way you can use the old name, but linker will see a different name;

char *my_strdup(const char *s) {
    char *p = malloc(strlen(s) + 1);
    if(p) { strcpy(p, s); }
    return p;
}

/* this goes in whatever header defines my_strdup */
char *my_strdup(const char *s);
#define strdup(x) my_strdup(x)
Evan Teran
  • 87,561
  • 32
  • 179
  • 238
  • good solution, but you need to wrap the #define of strdup(x) inside some other checking mechanisms to determine if it needs to be replaced. If not you probably want to use the libc strdup – Matt Aug 12 '09 at 18:54
  • 4
    There isn't much harm in *not* checking for that, except for possible optimizations. BTW, above might be faster with caching strlen and then using memcpy, because it is an intrinsic in many compilers, which ends up as a single CPU instruction. – Tim Čas May 29 '11 at 15:39
6

As Rob Kennedy noted the best way is to test inside your building scripts if this functions exists or not. I know that it is fairly easy with autoconfig, but probably with other cross-platform building scripts tools, too.

Then you simply place in your header file:


#ifndef HAVE_STRDUP
# ifdef HAVE__STRDUP
#  define strdup _strdup
# else
#  define strdup my_strdup
# endif
#endif

If strdup already exists on the target platform the libc version is used, if not your custom my_strdup function will be used.

EDIT: I should have added an explination why it is better.

First the compiler is unrelated to the existence of a function in the libc. For example take the function strlcpy. It is present on FreeBSD but not on Linux (glibc), although both are using gcc by default. Or what happens if someone is going to compile your code with clang?

Second a platform check (I don't know if there is a standard way) will only work if you explicitly add for every plattform you want to support the correct preprocessor conditional. So assuming you have mastered to compile your application on OSX and Win32 and you want to compile it now on Linux, you'll have to go through all preprocessor conditionals to see if they work for Linux. Maybe you also want to support FreeBSD, OpenBSD, etc.? Same work again. With a test in your building scripts, it may compile without any additional work.

quinmars
  • 11,175
  • 8
  • 32
  • 41
4

a) What happens when I write my own function with the same name as a built-in function?

You cannot re-define a function that already exists in a header file you are including. This will result in a compilation error.

b) What can I do to avoid bad things happening to me on platforms that don't have strdup() without rewriting all my code to not use strdup(), which is just a bit tedious?

I would recommend creating your own wrapper function to strdup, and replacing all your calls to use the new wrapper function. For example:

char *StringDuplicate(const char *s1)
{
#ifdef POSIX
    return strdup(s1);
#else
    /* Insert your own code here */
#endif
}

Changing all your calls from strdup to StringDuplicate() should be a simple find-and-replace operation, making it a feasible approach. The platform-specific logic will then be kept in a single location rather than being scattered throughout your codebase.

LeopardSkinPillBoxHat
  • 28,915
  • 15
  • 75
  • 111
3

You should also consider avoiding the creation of any identifier (including a function) that begins with str[a-z]. While this isn't reserved, the C standard (ISO/IEC 9899:1999) section 7.26.11 (future library directions) states "Function names that begin with str, mem, or wcs and a lowercase letter may be added to the declarations in the header."

Chris Young
  • 15,627
  • 7
  • 36
  • 42
2

FYI: I've never personally seen an environment that did not define strdup().

Eddie
  • 53,828
  • 22
  • 125
  • 145
  • 1
    i used to like strdup too - until i told that to guys in ##c at irc.freenode.org . they don't like it, and argue that if there is a two lines simple, guaranteed working way - why should one depend on not portable functions? (strdup is deprecated in windows, and win ce doesn't seem to have it) – Johannes Schaub - litb Jan 27 '09 at 05:47
  • msdn says one should use _strdup instead. but i think i read those deprecation warnings are nonsense anyway :) but today it was a surprise to me to see strdup actually is so broadly used. – Johannes Schaub - litb Jan 27 '09 at 05:49
-3

If anyone else reads this: Don't use a platform's strdup() even if available, and don't waste time/effort with autoconf/automake just to use it. Seriously, how hard is this:

char* mystrdup(const char* str)
{
 return strcpy(malloc( strlen(str) + 1),str);
}

Does this really warrant #ifdefs? Compiler checks? K.I.S.S.

Pat
  • 21