9

I'm creating a cross-system application. It uses, for example, the function itoa, which is implemented on some systems but not all. If I simply provide my own itoa implementation:

header.h:115:13: error: conflicting types for 'itoa'
 extern void itoa(int, char[]);

In file included from header.h:2:0,
                 from file.c:2:0,
c:\path\to\mingw\include\stdlib.h:631:40: note: previous declaration of 'itoa' was here
 _CRTIMP __cdecl __MINGW_NOTHROW  char* itoa (int, char*, int);

I know I can check if macros are predefined and define them if not:

#ifndef _SOME_MACRO
#define _SOME_MACRO 45
#endif

Is there a way to check if a C function is pre-implemented, and if not, implement it? Or to simply un-implement a function?

MD XF
  • 7,860
  • 7
  • 40
  • 71
  • 12
    There's the `autoconf` system — it's designed to deal with such complexities. There aren't many simpler systems that work reliably; I'm not sure I know of one. There's also `automake` and `libtool` that work in conjunction with `autoconf`, but you're mostly after `autoconf`. You could investigate other build systems such as `cmake`. – Jonathan Leffler Feb 18 '17 at 03:43
  • 2
    Another tricky part about a function that is only available on _some_ systems is that they tend to be too often _functionally different_ on different systems. I suggest a wrapper function. – chux - Reinstate Monica Feb 18 '17 at 10:25
  • @chux *wrapper function* explanation? – MD XF Feb 18 '17 at 15:53
  • 1
    _wrapper function_: Write `void my_itoa(int, char[]);` On systems with matching `itoa()` functionality/signature, have `my_itoa()` call `itoa()` , maybe using `inline` or `#define`. On platforms with `char * itoa (int value, char *str, int base);` like [this](https://www.mkssoftware.com/docs/man3/itoa.3.asp), call `itoa (value, str, 10);` . On so on with variant platforms. With systems lacking any `itoa()`-like function, call your own [code](http://stackoverflow.com/a/29544825/2410359). – chux - Reinstate Monica Feb 18 '17 at 16:53
  • easiest way to determine if a specific function is implemented: compile until a clean compile achieved, the link. If the link fails, then the function is not implemented within the scope of the libraries used. – user3629249 Feb 18 '17 at 16:58
  • 1
    You can't do this automatically; the C preprocessor has no access to what the C code is doing. But you can wrap your code in `#if` blocks that conditionally include your implementation: `#if NEED_ITOA` `int itoa(char *) { ... }` `#endif`. And then when compiling, you can `-DNEED_ITOA` for systems that don't have it. Under the hood, this is all that "magic" tools like `autoconf` really do (they also help to automatically figure out which `-D` flags you'll need). But there's no way to do this within the C preprocessor by itself. – Sean Werkema Mar 12 '17 at 17:14
  • 1
    The `autoconf` feature designed for this specific problem is [`AC_REPLACE_FUNCS`](https://www.gnu.org/savannah-checkouts/gnu/autoconf/manual/autoconf-2.69/html_node/Generic-Functions.html#Generic-Functions) (at the very bottom of the page, you need to read the entire page to understand what it does). – zwol Mar 13 '17 at 16:16
  • XY problem. The sensible way to solve the actual problem, is to give your own custom function a different name and then always use your version. And that's it. If your function is good enough to work in some cases, it should be good enough to work in all cases. If not, you need to improve the function. – Lundin Mar 16 '17 at 09:35

4 Answers4

6

Given you have already written your own implementation of itoa(), I would recommend that you rename it and use it everywhere. At least you are sure you will get the same behavior on all platforms, and avoid the linking issue.

Don't forget to explain your choice in the comments of your code...

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Heyji
  • 1,113
  • 8
  • 26
4

I assume you are using GCC, as I can see MinGW in your path... there's one way the GNU linker can take care of this for you. So you don't know whether there is an itoa implementation or not. Try this:

Create a new file (without any headers) called my_itoa.c:

char *itoa (int, char *, int);

char *my_itoa (int a, char *b, int c)
{
    return itoa(a, b, c);
}

Now create another file, impl_itoa.c. Here, write the implementation of itoa but add a weak alias:

char* __attribute__ ((weak)) itoa(int a, char *b, int c)
{
     // implementation here
}

Compile all of the files, with impl_itoa.c at the end.

This way, if itoa is not available in the standard library, this one will be linked. You can be confident about it compiling whether or not it's available.

MD XF
  • 7,860
  • 7
  • 40
  • 71
Ajay Brahmakshatriya
  • 8,993
  • 3
  • 26
  • 49
  • Doesn't work. I get `relocation truncated to fit: R_X86_64_PC32 against undefined symbol 'itoa'` – MD XF Mar 14 '17 at 20:11
  • @MDXF can you confirm whether you are only interested in MinGW? This is a good suggestion, unfortunately MinGW doesn't support it, – jerry Mar 15 '17 at 04:20
  • Well, this seems the best. Thanks. Will accept and bounty if no better solution arises. – MD XF Mar 16 '17 at 18:10
2

Ajay Brahmakshatriya's suggestion is a good one, but unfortunately MinGW doesn't support weak definition last I checked (see https://groups.google.com/forum/#!topic/mingwusers/44B4QMPo8lQ, for instance).

However, I believe weak references do work in MinGW. Take this minimal example:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

__attribute__ ((weak)) char* itoa (int, char*, int);

char* my_itoa (int a, char* b, int c)
{
    if(itoa != NULL) {
        return itoa(a, b, c);
    } else {
        // toy implementation for demo purposes
        // replace with your own implementation
        strcpy(b, "no itoa");
        return b;
    }        
}

int main()
{
    char *str = malloc((sizeof(int)*3+1));

    my_itoa(10, str, 10);
    printf("str: %s\n", str);
    return 0;
}

If the system provides an itoa implementation, that should be used and the output would be

str: 10

Otherwise, you'll get

str: no itoa

Community
  • 1
  • 1
jerry
  • 2,581
  • 1
  • 21
  • 32
  • @MDXF so you're seeing it work through cmd but not through cygwin? Might be a version difference (I'm on `gcc 4.9.3`), but it works on my system through at least cygwin. And by "works", I mean links successfully and prints `str: no itoa`. – jerry Mar 15 '17 at 18:17
  • @MDXF Just updated cygwin (with gcc version 5.4.0) and tested native MinGW, it works for both on my system. What version are you using? Is it 32 or 64 bit? – jerry Mar 16 '17 at 00:34
0

There are two really important related points worth making here along the "don't do it like this" lines:

  • Don't use atoi because it's not safe.
  • Don't use atoi because it's not a standard function, and there are good standard functions (such as snprintf) which are available to do what you want.

But, putting all this aside for one moment, I want to introduce you to autoconf, part of the GNU build system. autoconf is part of a very comprehensive, very portable set of tools which aim to make it easier to write code which can be built successfully on a wide range of target systems. Some would argue that autoconf is too complex a system to solve just the one problem you pose with just one library function, but as any program grows, it's likely to face more hurdles like this, and getting autoconf set up for your program now will put you in a much stronger position for the future.

Start with a file called Makefile.in which contains:

CFLAGS=--ansi --pedantic -Wall -W

program: program.o
program.o: program.c

clean:
  rm -f program.o program

and a file called configure.ac which contains:

AC_PREREQ([2.69])
AC_INIT(program, 1.0)
AC_CONFIG_SRCDIR([program.c])
AC_CONFIG_HEADERS([config.h])

# Checks for programs.
AC_PROG_CC

# Checks for library functions.
AH_TEMPLATE([HAVE_ITOA], [Set to 1 if function atoi() is available.])
AC_CHECK_FUNC([itoa],
              [AC_DEFINE([HAVE_ITOA], [1])]
              )

AC_CONFIG_FILES([Makefile])
AC_OUTPUT

and a file called program.c which contains:

#include <stdio.h>
#include "config.h"

#ifndef HAVE_ITOA

/*
 * WARNING: This code is for demonstration purposes only. Your
 * implementation must have a way of ensuring that the size of the string
 * produced does not overflow the buffer provided.
 */

void itoa(int n, char* p) {
  sprintf(p, "%d", n);
}
#endif

int main(void) {
  char buffer[100];
  itoa(10, buffer);
  printf("Result: %s\n", buffer);
  return 0;
}

Now run the following commands in turn:

  1. autoheader: This generates a new file called config.h.in which we'll need later.
  2. autoconf: This generates a configuration script called configure
  3. ./configure: This runs some tests, including checking that you have a working C compiler and, because we've asked it to, whether an itoa function is available. It writes its results into the file config.h for later.
  4. make: This compiles and links the program.
  5. ./program: This finally runs the program.

During the ./configure step, you'll see quite a lot of output, including something like:

checking for itoa... no

In this case, you'll see that the config.h find contains the following lines:

/* Set to 1 if function atoi() is available. */
/* #undef HAVE_ITOA */

Alternatively, if you do have atoi available, you'll see:

checking for itoa... yes

and this in config.h:

/* Set to 1 if function atoi() is available. */
#define HAVE_ITOA 1

You'll see that the program can now read the config.h header and choose to define itoa if it's not present.

Yes, it's a long way round to solve your problem, but you've now started using a very powerful tool which can help you in a great number of ways.

Good luck!

Community
  • 1
  • 1
Tim
  • 9,171
  • 33
  • 51