Enabling printf Breaks va_args

I recently followed the instructions here: http://playground.arduino.cc/Main/Printf for “Adding printf to Print.h” by editing Print.h. It worked just fine, until I used the modified Print.h to build an application that uses va_args. When I compile this application, I get the following errors:

SuperATC.ino:In function ‘void ConsolePrint(Stream&, char*, …)’
SuperATC.ino:242:2: error: ‘va_list’ was not declared in this scope
SuperATC.ino:242:10: error: expected ‘;’ before ‘args’
SuperATC.ino:243:12: error: ‘args’ was not declared in this scope

which refer to this function, which has been working perfectly for months:

void ConsolePrint(Stream &device,char *format,...)
{
	char buff[128];
	va_list args;
	va_start (args,format);
	vsnprintf(buff,sizeof(buff),format,args);
	va_end (args);
	buff[sizeof(buff)/sizeof(buff[0])-1]='\0';
	device.print(buff);
}

#if’ing out the printf code added to Print.h eliminates the errors, but I can’t understand why they occur. This is on a Due, if that matters. #include’ing <stdarg.h> to the top-level .ino file makes no difference.

BTW - Don’t ask me to post ALL the code - it’s dozens of source files, and dozens of libraries, and compiles to almost 250K of object code.

Regards,
Ray L.

Good news is, a trivial example fails in the same way:

#include <Arduino.h>
#include <stdarg.h>

void ConsolePrint(Stream &device,char *format,...)
{
	char buff[128];
	va_list args;
	va_start (args,format);
	vsnprintf(buff,sizeof(buff),format,args);
	va_end (args);
	buff[sizeof(buff)/sizeof(buff[0])-1]='\0';
	device.print(buff);
}


void setup()
{
	Serial.begin(115200);
	ConsolePrint(Serial, "Ready\n");
}


void loop()
{
}

Regards,
Ray L.

Hello, Ray.

Sadly I can’t help you with what are possible wrong, because my programing skills are quite basicly, but I did the same thing a while ago and everything work’s fine.
So, what I can do for you, is share my Print.h file (see the attachment).

void printf(const char *format, ...) {
      char buf[PRINTF_BUF];
      va_list ap;
      va_start(ap, format);
      vsnprintf(buf, sizeof(buf), format, ap);
      for (char *p = &buf[0]; *p; p++) {
        write(*p);
      }
      va_end(ap);
    }

I hope it work’s for you, or at least it helps you find the problem with your’s.

P.S: Reviewing your code, I think maybe the problem lays on you using the buff after the va_end call.

Print.h (3.95 KB)

Arank: Hello, Ray.

Sadly I can't help you with what are possible wrong, because my programing skills are quite basicly, but I did the same thing a while ago and everything work's fine. So, what I can do for you, is share my Print.h file (see the attachment).

void printf(const char *format, ...) {
      char buf[PRINTF_BUF];
      va_list ap;
      va_start(ap, format);
      vsnprintf(buf, sizeof(buf), format, ap);
      for (char *p = &buf[0]; *p; p++) {
        write(*p);
      }
      va_end(ap);
    }

I hope it work's for you, or at least it helps you find the problem with your's.

P.S: Reviewing your code, I think maybe the problem lays on you using the buff after the va_end call.

I get basically the same errors with your code.

This is something wonky in the build. The code has been working perfectly for a long time, until yesterday when I changed Print.h.

Regards, Ray L.

Just for completeness, here it my modified Print.h:

/*
  Print.h - Base class that provides print() and println()
  Copyright (c) 2008 David A. Mellis.  All right reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#ifndef Print_h
#define Print_h

#include <inttypes.h>
#include <stdio.h> // for size_t

#include "WString.h"
#include "Printable.h"

#define DEC 10
#define HEX 16
#define OCT 8
#define BIN 2

class Print
{
  private:
    int write_error;
    size_t printNumber(unsigned long, uint8_t);
    size_t printFloat(double, uint8_t);
  protected:
    void setWriteError(int err = 1) { write_error = err; }
  public:
    Print() : write_error(0) {}
  
    int getWriteError() { return write_error; }
    void clearWriteError() { setWriteError(0); }
  
    virtual size_t write(uint8_t) = 0;
    size_t write(const char *str) {
      if (str == NULL) return 0;
      return write((const uint8_t *)str, strlen(str));
    }
    virtual size_t write(const uint8_t *buffer, size_t size);
    size_t write(const char *buffer, size_t size) {
      return write((const uint8_t *)buffer, size);
    }
    
    size_t print(const __FlashStringHelper *);
    size_t print(const String &);
    size_t print(const char[]);
    size_t print(char);
    size_t print(unsigned char, int = DEC);
    size_t print(int, int = DEC);
    size_t print(unsigned int, int = DEC);
    size_t print(long, int = DEC);
    size_t print(unsigned long, int = DEC);
    size_t print(double, int = 2);
    size_t print(const Printable&);

    size_t println(const __FlashStringHelper *);
    size_t println(const String &s);
    size_t println(const char[]);
    size_t println(char);
    size_t println(unsigned char, int = DEC);
    size_t println(int, int = DEC);
    size_t println(unsigned int, int = DEC);
    size_t println(long, int = DEC);
    size_t println(unsigned long, int = DEC);
    size_t println(double, int = 2);
    size_t println(const Printable&);
    size_t println(void);

#if 1		// Set to 0 to disable printf
#include <stdarg.h>
#define PRINTF_BUF 80 // define the tmp buffer size (change if desired)
   void printf(const char *format, ...)
   {
   char buf[PRINTF_BUF];
   va_list ap;
	va_start(ap, format);
	vsnprintf(buf, sizeof(buf), format, ap);
	for(char *p = &buf[0]; *p; p++) // emulate cooked mode for newlines
	{
		if(*p == '\n')
			write('\r');
		write(*p);
	}
	va_end(ap);
   }
#ifdef F // check to see if F() macro is available
   void printf(const __FlashStringHelper *format, ...)
   {
   char buf[PRINTF_BUF];
   va_list ap;
	va_start(ap, format);
#ifdef __AVR__
	vsnprintf_P(buf, sizeof(buf), (const char *)format, ap); // progmem for AVR
#else
	vsnprintf(buf, sizeof(buf), (const char *)format, ap); // for the rest of the world
#endif
	for(char *p = &buf[0]; *p; p++) // emulate cooked mode for newlines
	{
		if(*p == '\n')
			write('\r');
		write(*p);
	}
	va_end(ap);
   }
#endif
#endif
};

#endif

Regards,
Ray L.

I'm the one that added the printf stuff to the Print page. I'm assuming that the header files have been changed and something is not getting properly included. Are you building the code with the warnings enabled? (The IDE ships with them all disabled - which I think is VERY dumb as it silently hides many potential issues) [Home]->Preferences Show verbose output during [x] compilation

I'll correct and update the Print page if I can replicate your issue.

What version of the IDE and what processor are you using when you get the error?

bperrybap: I'm the one that added the printf stuff to the Print page. I'm assuming that the header files have been changed and something is not getting properly included. Are you building the code with the warnings enabled? (The IDE ships with them all disabled - which I think is VERY dumb as it silently hides many potential issues) [Home]->Preferences Show verbose output during [x] compilation

I'll correct and update the Print page if I can replicate your issue.

What version of the IDE and what processor are you using when you get the error?

Excellent! I'm currently running 1.6.1, under Atmel Studio/VisualMicro, on a Due. Below is the build output from the test program above, with -Wall and verbose turned on. Output from the Arduino IDE is essentially identical.

Compiling 'BlivetSketch' for 'Arduino Due (Programming Port)' Build folder: file:///C:/Users/RayL/AppData/Local/V.Micro/Arduino/Builds/BlivetSketch/arduino_due_x_dbg Summary: Header=1 Prototypes=3 Imports=3 Additional Defines: Architecture Tools: c:\Arduino\Arduino-1.6.1/hardware/tools/gcc-arm-none-eabi-4.8.3-2014q1/bin/ Sketchbook: file:///C:/Users/RayL/Documents/Arduino Core Include Paths Include Path 'c:\Arduino\Arduino-1.6.1\hardware\arduino\sam\cores\arduino' Include Path 'c:\Arduino\Arduino-1.6.1\hardware\arduino\sam\variants\arduino_due_x' "c:\Arduino\Arduino-1.6.1/hardware/tools/gcc-arm-none-eabi-4.8.3-2014q1/bin/arm-none-eabi-g++" -c -g -Os -Wall -ffunction-sections -fdata-sections -nostdlib -fno-threadsafe-statics --param max-inline-insns-single=500 -fno-rtti -fno-exceptions -Dprintf=iprintf -MMD -mcpu=cortex-m3 -DF_CPU=84000000L -DARDUINO=161 -DARDUINO_SAM_DUE -DARDUINO_ARCH_SAM -D_SAM3X8E_ -mthumb -DUSB_VID=0x2341 -DUSB_PID=0x003e -DUSBCON -DUSB_MANUFACTURER="\"Unknown\"" -DUSB_PRODUCT="\"Arduino Due\"" "-Ic:\Arduino\Arduino-1.6.1\hardware\arduino\sam\system/libsam" "-Ic:\Arduino\Arduino-1.6.1\hardware\arduino\sam\system/CMSIS/CMSIS/Include/" "-Ic:\Arduino\Arduino-1.6.1\hardware\arduino\sam\system/CMSIS/Device/ATMEL/" -I"c:\Arduino\Arduino-1.6.1\hardware\arduino\sam\cores\arduino" -I"c:\Arduino\Arduino-1.6.1\hardware\arduino\sam\variants\arduino_due_x" "C:\Users\RayL\AppData\Local\V.Micro\Arduino\Builds\BlivetSketch\arduino_due_x_dbg\BlivetSketch.cpp" -o "C:\Users\RayL\AppData\Local\V.Micro\Arduino\Builds\BlivetSketch\arduino_due_x_dbg\BlivetSketch.cpp.o" BlivetSketch.ino:In function 'void ConsolePrint(Stream&, char*, ...)' BlivetSketch.ino:9:2: error: 'va_list' was not declared in this scope BlivetSketch.ino:9:10: error: expected ';' before 'args' BlivetSketch.ino:10:12: error: 'args' was not declared in this scope BlivetSketch.ino:In function 'void setup()' BlivetSketch.ino:31:32: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings] Error compiling

Thanks for looking into this!

Regards, Ray L.

The error looked like a simple case of not getting varargs declarations which is supposed to be handled by stdarg.h

I have a library (openGLCD) that does varargs to create printfxx() routines it is working when built for DUE under the 1.6.1 IDE. I also took the exact sample sketch you provided in post #1 (cut and pasted it into a .ino file) and it also compiles without any issues with the 1.6.1. IDE.

I can't replicate your exact environment since I don't use windows so there are differences there.

What I did notice is that there are two stdarg.h header files down under the arm tools that come with the IDE. The two header files are not the same. They are both down under hardware/tools ....... If you search your directory tree you will find them. One of them has the vararg stuff in it like va_list and one doesn't. Maybe your environment is not setting up something to be grabbing the correct one?

I'm assuming this is something related to your environment since it is working for me as well as Arank.

You could put a conditional like a #warning or #error down in the stdarg.h header files just to verify which one you are using.

--- bill

Odd… My build is using stdarg.h from:

C:\Arduino\Arduino-1.6.1\hardware\tools\gcc-arm-none-eabi-4.8.3-2014q1\lib\gcc\arm-none-eabi\4.8.3\include

That one does define va_list, though it would take me a week to make sense of the definition…

The other stdarg.h is in:

C:\Arduino\Arduino-1.6.1\hardware\tools\gcc-arm-none-eabi-4.8.3-2014q1\arm-none-eabi\include\c++\4.8.3

That one is just a shell, which looks like this (comments deleted):

/** @file tr1/stdarg.h
 *  This is a TR1 C++ Library header. 
 */

#ifndef _TR1_STDARG_H
#define _TR1_STDARG_H 1

#include <tr1/cstdarg>

#endif

tr1/cstdarg looks like this (comments deleted):

/** @file tr1/cstdarg
 *  This is a TR1 C++ Library header. 
 */

#ifndef _GLIBCXX_TR1_CSTDARG
#define _GLIBCXX_TR1_CSTDARG 1

#include <cstdarg>

#endif // _GLIBCXX_TR1_CSTDARG

cstdarg looks like this (comments deleted):

//
// ISO C++ 14882: 20.4.6  C library
//

#pragma GCC system_header

#include <bits/c++config.h>
#include <stdarg.h>

#ifndef _GLIBCXX_CSTDARG
#define _GLIBCXX_CSTDARG 1

// Adhere to section 17.4.1.2 clause 5 of ISO 14882:1998
#ifndef va_end
#define va_end(ap) va_end (ap)
#endif

namespace std
{
  using ::va_list;
} // namespace std

#endif

This just gives me a headache… Seems very convoluted.

Regards,
Ray L.

This appears to be something broken in 1.6.1. I just installed 1.6.4, and all compiles fine now!

Regards, Ray L.

But my 1.6.1 worked. Strange....

bperrybap: But my 1.6.1 worked. Strange....

bperrybap: But my 1.6.1 worked. Strange....

Looks like I was a bit premature, and forgot to edit Print.h after installing 1.6.4. And, as it turns out, in 1.6.4 there is no Due-specific Print.h. Or, if there is, it's in a completely different place, and I can't find it. 1.6.4 does not, by default, include ANY Due support. That has to be installed separately. I don't know where it goes, but it has clearly moved to a different location from 1.6.1. Any help in finding it, and figuring out how to enable printf would be greatly appreciated.

Regards, Ray L.

I hadn't run 1.6.4 yet. They have debundled the DUE stuff which can be added back in using the boards manager. But the way they handle it is total BULLSHIT!!!!! They are installing it under the users .arduino directory vs under the users sketchbook which is where the user wants arduino related stuff to be placed. This is total CRAP!

I test my openGLCD library against multiple IDEs (15+) and the way they are handling this breaks being able to do this since you no longer have an atomic toolset. The bigger problem is where they are installing their stuff. They should be installing it just like any other 3rd party core which goes under the users sketchbook/hardware directory not under the users .arduino directory.

The reason this really matters is that the user may choose to relocate his IDEs installs and sketchbook areas to a different area than his home directory. This could be done for a variety of reasons including space reasons. In my case it is done to be able to allow everything to be remote mounted which allows everything to be shared and run from many different machines, including across different operating systems.

The IDE allows the user to specify where his sketchbook is located and if the IDE is not going to install the add on board packages with their associated tools in the IDE tree (which I think it should), then it should at least install them down under the users sketchbook area under the hardware directory just like any other 3rd party core. To put it down under the users .arduino directory really sucks, and in my view is placing an unwanted gigantic turd down under the users home directory.

I will immediately bring up this issue to the Arduino team. As soon as I figure out how to de-crapify my system from installing the 1.6.4 DUE support

BTW, I did try your test vararg sketch and it did work on 1.6.4 as well.

--- bill

bperrybap: I hadn't run 1.6.4 yet. They have debundled the DUE stuff which can be added back in using the boards manager. But the way they handle it is total BULLSHIT!!!!! They are installing it under the users .arduino directory vs under the users sketchbook which is where the user wants arduino related stuff to be placed. This is total CRAP!

I test my openGLCD library against multiple IDEs (15+) and the way they are handling this breaks being able to do this since you no longer have an atomic toolset. The bigger problem is where they are installing their stuff. They should be installing it just like any other 3rd party core which goes under the users sketchbook/hardware directory not under the users .arduino directory.

The reason this really matters is that the user may choose to relocate his IDEs installs and sketchbook areas to a different area than his home directory. This could be done for a variety of reasons including space reasons. In my case it is done to be able to allow everything to be remote mounted which allows everything to be shared and run from many different machines, including across different operating systems.

The IDE allows the user to specify where his sketchbook is located and if the IDE is not going to install the add on board packages with their associated tools in the IDE tree (which I think it should), then it should at least install them down under the users sketchbook area under the hardware directory just like any other 3rd party core. To put it down under the users .arduino directory really sucks, and in my view is placing an unwanted gigantic turd down under the users home directory.

I will immediately bring up this issue to the Arduino team. As soon as I figure out how to de-crapify my system from installing the 1.6.4 DUE support

BTW, I did try your test vararg sketch and it did work on 1.6.4 as well.

--- bill

I share your pain.

You indicate the Due stuff has been moved under the users .arduino directory? I can't find that. Exactly what directory are you referring to? I don't see any ".arduino" directory anywhere.

To me, the way it was done under 1.6.1 was correct - ALL the tools for a given version should be in one place, and it should be easy to have multiple versions available at one time. I have to run multiple versions of the tools, and I HAVE had to re-locate them at times, so this is critical. Now, I don't even know where they put the Due stuff, and this is at least the third time a minor version update, to circumvent some crippling bug, has caused me hours of grief!

Regards, Ray L.

There is a .arduino15 directory under the users home directory that has existed ever since the 1.5x IDE development forked off from the 1.x code. That is where preferences.txt is and that is now where there is a "packages" directory where all this new stuff is being installed.

You can see the path to it at the bottom of the preferences dialog box.

There is so much on-the-fly "development" going on right now that I'm seeing many less than desirable decisions being made. While it is good to be nimble, trying to move too quickly can create lots of issues and that is what I'm seeing ever since the arduino.org fight started. The Arduino LLC guys are trying to move quickly and be responsive to out develop the arduino.org guys to keep users following them vs arduino.org, but in my opinion arduino LCC are simply moving too quickly right now without putting enough thought behind some of the changes.

OK, I found it. In Windows, it's under:

C:\Users\Username\AppData\Roaming\Arduino15\packages\arduino\hardware\sam\1.6.4

Regards, Ray L.