Introduction

When compiling a source file you see an error diagnostic similar to ...

"file.c", line 5: error: too few arguments in function call

When you look at the problem source line, you see it is a call to the function ltoa. How should this error be handled?

Overview

Some background on the ltoa function is given. The change is explained. Alternatives to ltoa are reviewed. Three methods are shown for how you can change your code to fix the error.

Background

The function ltoa takes a long integer value and converts it into an ASCII string. It is not among the runtime support functions required by the ANSI standard for C. Even so, TI has supplied an ltoa function for many years. It started as a helper to the printf family of functions. It was never documented.

The interface to ltoa changed. In older versions of TI compilers, this is the interface ...

int ltoa(long val, char *buffer);               /* OLD */

In newer versions of TI compilers, the interface is different ...

char *ltoa(long val, char *buffer, int radix);  /* NEW */

The older ltoa returns an int, and takes 2 arguments. The newer ltoa returns a pointer to char, and takes 3 arguments. The newer ltoa matches the implementation that comes with many other commonly used C compilers.

Version Information

The change to ltoa is introduced in these versions of the compiler.

Target Version
ARM 19.6.0.STS
MSP430 19.6.0.STS
C28x 19.6.0.STS
ARP32 1.1.0
PRU 2.3.2
C6000 8.2.6
C6000 8.3.3

For C6000, in the series of version 8.2.x releases, the change is introduced in version 8.2.6. For C6000, in the series of version 8.3.x releases, the change is introduced in version 8.3.3.

As of this writing, ARP32 compiler version 1.1.0 is not available.

Alternatives to ltoa

It may make sense to replace a call to ltoa with one of these alternatives.

  • sprintf
  • A custom implementation with a different name

Changing Your Code

How might you change your code that calls ltoa to account for this change? Three methods are shown, each more elaborate than the previous. Use the one that best fits your needs.

Method One

Presumptions

  • Return value is not used
  • Portability is not a concern
    • Between different compilers
    • Between different versions of the same compiler

Details

In this case, add the value 10 as the third argument.

ltoa(value, local_buffer);     /* OLD */
ltoa(value, local_buffer, 10); /* NEW */

The third argument to the new ltoa is the radix used when converting to the string. The implied radix of the old ltoa is 10, for decimal numbers.

Method Two

Presumptions

  • Only one compiler is used
  • The compiler is a TI compiler
  • Must be portable between different versions of that compiler

Details

The goal of this method is to create the preprocessor symbol OLD_LTOA, or the preprocessor symbol NEW_LTOA.

All TI compilers support a predefined preprocessor symbol named __TI_COMPILER_VERSION__. It contains, in decimal number form, an encoding of the version of the compiler. This table shows, for each target CPU, the value of __TI_COMPILER_VERSION__ which introduces the change to ltoa.

Target TCV Value
ARM 19006000
MSP430 19006000
C28x 19006000
ARP32 1001000
PRU 2003002
C6000 Details below

TCV, in the second column title, stands for TI Compiler Version.

The following example is for the TI ARM compiler. To adapt it to another compiler, replace 19006000 with the TCV Value from the table above.

In a header file which is included by all source files that call ltoa, have lines similar to the following:

#if __TI_COMPILER_VERSION__ < 19006000
#define OLD_LTOA 
#else
#define NEW_LTOA
#endif

A call to ltoa could be changed to something similar to ...

#ifdef OLD_LTOA
ltoa(value, local_buffer);
#else
ltoa(value, local_buffer, 10);
#endif

To understand the details of how the __TI_COMPILER_VERSION__ numbers work, please search the relevant target compiler manual for the sub-chapter titled Predefined Macro Names.

Details for C6000

Since the change is introduced on two different streams of related versions of the compiler, the version comparison is a bit more complicated.

/* Create a shorter name, so the comparison is easier to read */
#define TCV __TI_COMPILER_VERSION__

#if TCV < 8002006 || (8003000 <= TCV && TCV <= 8003002)
#define OLD_LTOA
#else
#define NEW_LTOA
#endif

/* No longer need the shorter name */
#undef TCV

Method Three

Presumptions

  • Any compiler may be used
  • TI and non-TI compilers
  • Any version

Details

The goal of this method is, for non-TI compilers, to do nothing. If it is a TI compiler, then create the preprocessor symbol OLD_LTOA, or the preprocessor symbol NEW_LTOA.

In a header file which is included by all source files that call ltoa, have lines similar to the following:

/* Do nothing for a non-TI compiler */
#if defined(__TI_COMPILER_VERSION__)

/* Create a shorter name, to make the comparison easier to read */
#define TCV __TI_COMPILER_VERSION__

#if    (defined(__TI_ARM__)      && TCV < 19006000)                \
    || (defined(__MSP430__)      && TCV < 19006000)                \
    || (defined(__TMS320C2000__) && TCV < 19006000)                \
    || (defined(__ARP32__)       && TCV < 1001000)                 \
    || (defined(__PRU__)         && TCV < 2003002)                 \
    || (defined(__TMS320C6X__)   &&                                \
           (TCV < 8002006 || (8003000 <= TCV && TCV <= 8003002)))
#define OLD_LTOA
#else
#define NEW_LTOA
#endif

/* No longer need the shorter name */
#undef TCV

#endif /* defined(__TI_COMPILER_VERSION__) */

The preprocessor symbols __TI_ARM__, __MSP430__, etc. are predefined by those particular TI compilers.