NXC: ANSI printf like function printfEx also for \n, %s

Discussion specific to NXT-G, NXC, NBC, RobotC, Lejos, and more.
Post Reply
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

NXC: ANSI printf like function printfEx also for \n, %s

Post by HaWe »

just to share:
these new macros
printfEx()
together with scrclr()
provide a printf() more like ANSI C with support of "\n"
(but only for 1 variable each):

Code: Select all

int __CURSOR_X__=0, __CURSOR_Y__=56;

// scrclr() in conio.h
#define scrclr() { \
  ClearScreen(); \
  __CURSOR_X__=0; __CURSOR_Y__=56; \
}

#define printf1( _x, _y, _format1, _value1) {  \
   __CURSOR_Y__=_y;            \
  string sval1 = FormatNum(_format1, _value1); \
  TextOut(_x, _y, sval1); \
  __CURSOR_X__=_x+6*(strlen(sval1));   \
}

// printf() in stdio.h
#define printfEx(_format1, _value1) {           \
  int _x=__CURSOR_X__; int _y=__CURSOR_Y__;     \
  int posn, posf, slen, rlen;   \
  string rstr, lstr;            \
  slen=strlen(_format1);        \
  posn=Pos("\n", _format1);     \
  posf=Pos("%", _format1);      \
  if (posn>=0) {                     \
    rlen=slen-posn-1;                  \
    lstr= LeftStr(_format1,posn);      \
    rstr= RightStr(_format1,rlen);     \
    strcat(lstr,rstr);                 \
    if(posn<posf) {                    \
      if(_y>=8)_y-=8; _x=0;}           \
    printf1(_x, _y, lstr, _value1);    \
    if(posf<posn) {                    \
      if(__CURSOR_Y__>=8)  __CURSOR_Y__-=8; \
      __CURSOR_X__=0;}                      \
  }                                         \
  else printf1(_x, _y, _format1, _value1);  \
}


//********************************************************************

task main()
{
  printfEx("%s", "text 1");
  printfEx("\n%s", "LF => text 2");

  printfEx("\n%d ", 1);
  printfEx("%d  (LF 1 2 LF)\n", 2);
  printfEx("%d ", 3);
  printfEx("%d ", 4);
  printfEx("\n","");
  printfEx("\n","");
  printfEx("\n","");
  printfEx("\n","");
  printfEx("\n","");
  printfEx("\n","");
  printfEx("\n","");   // only valid if Cursor pos > last line!
  
  printfEx("\n%s","press BtnCenter");


  while(!ButtonPressed(BTNCENTER,0));
  scrclr();
  printfEx("\n%d ", 1);
  printfEx("%d  (LF 1 2 LF)\n", 2);
  printfEx("%d ", 3);
  printfEx("%d ", 4);

  while (1);
}
Last edited by HaWe on 09 Jun 2013, 15:37, edited 5 times in total.
spillerrec
Posts: 358
Joined: 01 Oct 2010, 06:37
Location: Denmark
Contact:

Re: NXC: new function printfEx also for "\n"

Post by spillerrec »

You shouldn't make such an long macro function! Do this as an normal function as otherwise the filesize will increase too much. (Just this simple program is over 9000 bytes.)
As a rule of thumb, if you have over twice the amount of statements than parameters, consider using a normal function.

I have considered before to make a proper console with automatically scrolls when last line is reached. Would you be interested in it?
My blog: http://spillerrec.dk/category/lego/
RICcreator, an alternative to nxtRICeditV2: http://riccreator.sourceforge.net/
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: NXC: new function printfEx also for "\n"

Post by HaWe »

I can't use
inline void printfEx()
because I couldn't pass u/s char, u/s int, u/s long, float, and strings to it if I did.
Unfortunately we don't have function overloading ;)

I guess you use a text screen buffer for scrolling? At least I've been thinking about doing this, but then maybe important values would be scrolled away too soon too far, and it needs additionally precious RAM (depending on the number of buffered pages of course).
In the moment my btnhit() together with scrclr() works like "press button to continue", I think that's fine for the moment, thx for your offer!

H.

ps:
anyhow, although the multi-variant variables are the mean point:
is it really such a big difference between inline void and #define ?

pps: of course you're allowed to tweak my code if the file will become much smaller then! :)
spillerrec
Posts: 358
Joined: 01 Oct 2010, 06:37
Location: Denmark
Contact:

Re: NXC: new function printfEx also for "\n"

Post by spillerrec »

Function overloading shouldn't be needed. The function which needs to draw it onto the screen should only need one parameter, the string to display. So your define should look like this:

Code: Select all

#define printfEx(_format1, _value1) print( FormatNum( _format1, _value1 ) )
The print() function will then find the line-breaks and display it on the screen. (print() should NOT be inline however!)
I tried to do it quickly and the rxe size dropped from ~9700 bytes to ~2500 bytes using optimization level 3. It didn't work exactly as it should, but it illustrates my point never the less. It also finished compiling much faster.
I guess you use a text screen buffer for scrolling? At least I've been thinking about doing this, but then maybe important values would be scrolled away too soon too far, and it needs additionally precious RAM
Yes, a screen buffer is required. However even with 20 lines, it would still max be 420 bytes and properly a lot less in general usage. I have rarely reached the 32KB limit so it is not something I worry about.
My blog: http://spillerrec.dk/category/lego/
RICcreator, an alternative to nxtRICeditV2: http://riccreator.sourceforge.net/
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: NXC: new function printfEx also for "\n"

Post by HaWe »

my macro function already has just 2 parameters :
#define printfEx(_format1, _value1) { }
and _value1 can be all sorts of ints, floats, and strings.
My remark about function overloading was referring to your proposal to use a function instead of a macro, and for such a function with 1 parameter for all sorts of ints, floats, and strings you obviously will need function overloading.

My printfEx is a wrap around printf1, which uses printf itself, of course I can copy this related code inside printfEx, too, If it will make the required mem much smaller.
On the other hand, I will need to have printf1 in future always additionally as well, anyway (it's my common substitute for both TextOut and NumOut plus formatting numbers, but that's not the issue).

All code must be safe for multithreading.

Did you try your own ideas about "\n" already?
I don't see yet how your idea will lead to a code that needs significantly less memory than mine
I also don't see how your first idea will work at all using a function instead of a #define (and also needing significantly less memory).
Can you show me both ways how to do?
Though my recent code is working fine for me, approvements for needing significantly less memory are appreciated.

Please always post your whole code! Your own printfEx should work with my task-main-test-code:

Code: Select all

task main()
{
  printfEx("%s", "text 1");
  printfEx("\n%s", "LF => text 2");

  printfEx("\n%d ", 1);
  printfEx("%d  (LF 1 2 LF)\n", 2);
  printfEx("%d ", 3);
  printfEx("%d ", 4);
  printfEx("\n","");
  printfEx("\n","");
  printfEx("\n","");
  printfEx("\n","");
  printfEx("\n","");
  printfEx("\n","");
  printfEx("\n","");   // only valid if Cursor pos > last line!
 
  printfEx("\n%s","press BtnCenter");


  while(!ButtonPressed(BTNCENTER,0));
  scrclr();
  printfEx("\n%d ", 1);
  printfEx("%d  (LF 1 2 LF)\n", 2);
  printfEx("%d ", 3);
  printfEx("%d ", 4);

  while (1);
}
(BTW, Scrolling is no issue for me, this thread is just about my idea how to use the "\n" parameter to have a more C-like printf, but you may share your scrolling library in a different thread if you like)
muntoo
Posts: 834
Joined: 01 Oct 2010, 02:54
Location: Your Worst Nightmare
Contact:

Re: NXC: new function printfEx also for "\n"

Post by muntoo »

Spiller's print() function is like my WordWrap() function.

Code: Select all

/*  Program Name: "WordWrap.nxc"
**  Author: muntoo
**  Programmed In: NXC

Description:
Word Wrap API.
*/


#ifndef __WORD_WRAP_NXC__
#define __WORD_WRAP_NXC__


#include "PUSH.nxc"


// WordWrap WrapOptions Flags                                                   REVISE THESE FLAGS
/*
#define WORD_WRAP_ALIGN_LEFT		0x01
#define WORD_WRAP_ALIGN_RIGHT		0x02
#define WORD_WRAP_ALIGN_CENTER		0x03  // (WORD_WRAP_ALIGN_LEFT | WORD_WRAP_ALIGN_RIGHT)
*/
#define WORD_WRAP_WRAP_BY_WORD		0x04


void strToLines(string &lines[], string szOut, byte charactersPerRow = 16, byte WrapOptions = WORD_WRAP_WRAP_BY_WORD)
{
	unsigned int szOut_len = strlen(szOut);

	unsigned int lines_len = 0;
	ArrayInit(lines, "", 0);


	unsigned int i2 = 0;
	for(unsigned int i = 0; i < szOut_len; ++i)
	{
		for(i2 = i; i2 < szOut_len; ++i2)
		{
			if(szOut[i2] == '\n')
				break;

			if((i2 - i + 1) >= charactersPerRow)
				break;
		}

		// If (charactersPerRow + 1) character is a newline, increase i2.
		if(((i2 - i + 1) == charactersPerRow) &&
		   ((i2 + 1) < szOut_len ? szOut[i2 + 1] == '\n' : false))
			++i2;

		if(WrapOptions & WORD_WRAP_WRAP_BY_WORD)
		{
			if((szOut[i2] != '\n') && (i2 < szOut_len))
			{
				unsigned int i2_bak = i2;

				// Backtrack to last space.
				for(; i2 > i; --i2)
				{
					if(szOut[i2] == ' ')
						break;
				}

				// If no space in line, undo backtrack.
				if(i2 == i)
				{
					i2 = i2_bak;
					// i2 = i + charactersPerRow;
				}
			}
		}

		string line = SubStr(szOut, i, i2 - i + 1);

		// Push new string onto stack.
		PUSH(string, lines, line)
		++lines_len;

		i = i2;
	}


	// Deinitialize.
	szOut = "";
	// ArrayInit(lines, 0, 0); // No! Do not!
}


void WordWrapOut(string szOut, int x, int y, byte charactersPerRow, byte WrapOptions, byte DisplayOptions)
{
	unsigned int szOut_len = strlen(szOut);

	string lines[];
	unsigned int lines_len = 0;


	// If out of bounds, return.
	if((x > 93) || (y < 7))
		return;


	// if charactersPerRow is NULL, it is calculated to be as long as possible without going off the right side of the screen
	if(charactersPerRow == 0)
		charactersPerRow = (99 - x) / 6;


	// Split szOut into lines.
	strToLines(lines, szOut, charactersPerRow, WrapOptions);
	lines_len = ArrayLen(lines);


	// DRAW
	// Preprocess DisplayOptions.
	if((DisplayOptions & DRAW_OPT_CLEAR_WHOLE_SCREEN) ||
	   (DisplayOptions & DRAW_OPT_CLEAR_EXCEPT_STATUS_SCREEN))
	{
		ClearScreen();

		// Remove clear screen bitflags from DisplayOptions.
		DisplayOptions = DisplayOptions & (~(DRAW_OPT_CLEAR_WHOLE_SCREEN | DRAW_OPT_CLEAR_EXCEPT_STATUS_SCREEN));
	}


	y -= 7;
	for(unsigned long i = 0; (i < lines_len) && (y > -8); ++i) // y >= 0
	{
		TextOut(x, y - (i * 8), lines[i], DisplayOptions);
	}


	// Deinitalize.
	szOut = "";
	ArrayInit(lines, "", 0);
}


#endif
PUSH.nxc:

Code: Select all

/*  Program Name: "PUSH.nxc"
**  Author: muntoo
**  Programmed In: NXC

Description:
Push stuff onto your 'stack'.
*/


#ifndef __PUSH_NXC__
#define __PUSH_NXC__


#if 1
	#define PUSH(type,out,val) \
		{ \
			type out##_tmp[]; \
			asm \
			{ \
				arrbuild out##_tmp, out, val \
				mov out, out##_tmp \
				arrinit out##_tmp, 0, 0 \
			} \
		}
#else
	// Causes File error -1!
	#define PUSH(type,out,val) ArrayBuild(out,out,val);
#endif


#define PUSH_SPECIAL(type,out,val,len) \
	if(len >= ArrayLen(out))\
	{ \
		type out##_tmp[]; \
		asm \
		{ \
			arrbuild out##_tmp, out, val \
			mov out, out##_tmp \
			arrinit out##_tmp, 0, 0 \
		} \
		++len; \
	} \
	else \
	{ \
		out[len] = val; \
		++len; \
	}


#define POP_SPECIAL(len) --len;


#endif
Now:

Code: Select all

#define printfEx(_format, _val) WordWrapOut(FormatNum(_format, _val), 0, 64, 16, WORD_WRAP_WRAP_BY_WORD, DRAW_OPT_NORMAL)
Image

Commit to LEGO Mindstorms Robotics Stack Exchange:
bit.ly/MindstormsSE


Commit to LEGO Stack Exchange: bit.ly/Area51LEGOcommit
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

new printf like function printfEx also for \n, %s

Post by HaWe »

mun2,
I can't see yet how your code works with parameter "\n".
Could you plz put all your printfEx code supporting also "\n" together with my test-task-main into 1 single file ready to compile?
I also haven't seen any working code published by spillerec.
Last edited by HaWe on 03 Sep 2012, 18:12, edited 1 time in total.
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: NXC: ANSI printf like function printfEx also for \n, %s

Post by HaWe »

a more enhanced printf (printfEx) which supports ANSI standards like %s, multiple \n (CR+LF), multiple \t (tabs) plus a feature \i for writing inverted:
just a gimmick - share and enjoy ! (who wants to)

Code: Select all

// nxcstdio.h
// providing a more ANSI C printf() compatible procedure

int _cur_x_=0, _cur_y_=56;      // cursor home for NXC  = upper left = (0,56)
int _tab_width_=24;             // tab width by default = 24 = 4* 6-point letter

unsigned long _TEXTSTYLE_ = DRAW_OPT_NORMAL;   // text style by default

#define scrclr() {  ClearScreen();  _cur_x_=0; _cur_y_=56; }

#define gotoxy( x, y )    { _cur_x_=x; _cur_y_=y; }   // move cursor to position
#define curxy( x, y )     { _cur_x_=x; _cur_y_=y; }   // move cursor to (alias)
#define curx  ( x )       { _cur_x_=x; }              // move cursor to x-pos
#define cury  ( y )       { _cur_y_=y; }              // move cursor to y-pos
#define curhome           { _cur_x_=0; _cur_y_=56; }  // move cursor home
#define settabwidth( t )  { _tab_width_ = t; }        // redefine tab width
#define settextstyle( t ) { _TEXTSTYLE_ = t; }        // redefine text style


inline string strsplit(string &src, string mark) {
  string s="";
  int p=-1, l;
  p=Pos(mark, src);
  if (p>=0) {
    l=strlen(mark);
    s= SubStr(src, 0, p);
    src=SubStr(src, p+l, strlen(src)-p);
  }
  return s;
}


string strexch(string src, string ex, string ch) {
  string s;
  s=strsplit(src,ex);
  return (StrCat(s,ch,src));
}


// printfxy()
// featuring "\i" for writing inverted
//******************************************************************************
#define printfxy( x, y, f, v) {      \
  _cur_y_=y;   string s1, s2, sv;    \
  s2=f;                              \
  if (Pos("\i",s2)>=0) {             \
    _TEXTSTYLE_= DRAW_OPT_INVERT;    \
    s2=strexch(s2,"\i","");          \
  }                                  \
  int len=0;                         \
  if (Pos("%",s2)==-1)  { sv=s2; }   \
  else { sv = FormatVal(s2, v);  }   \
  TextOut(x, y, sv, _TEXTSTYLE_);    \
  len=strlen(sv);                    \
  _cur_x_=x+6*(len);                 \
  _TEXTSTYLE_= DRAW_OPT_NORMAL;      \
}


// printfEx redefined as printf()
// featuring \n, \t, and "\i" for writing inverted
//******************************************************************************
#define printf(fmt, val) {            \
  int x=_cur_x_; int y=_cur_y_;       \
  string sf, s;                       \
  sf=fmt;                             \
  while (Pos("\n",sf)>=0) {           \
    s=strsplit(sf,"\n");              \
    while (Pos("\t",s)>=0) {          \
      x=(1+x/_tab_width_)*_tab_width_;\
      s=strexch(s, "\t", ""); }       \
    printfxy( x, y, s, val);          \
    x=0;  y-=8;                       \
  }                                   \
  while (Pos("\t",sf)>=0) {           \
      x=(1+x/_tab_width_)*_tab_width_;\
      sf=strexch(sf, "\t", ""); }     \
  printfxy( x, y, sf, val);           \
}



//******************************************************************************

task main()
{
  settabwidth(30);                               // tab width = 24 by default!

  int a=4; float b=1.2345;                       // featuring "%d","%f","%c"
  printf("%3d",a); printf(" - %7.4f\n=", b);     // featuring "\n": CR+LF
  printf("\n%4.2f! \n\n\n", a-b);
  printf("hello,%s!\n", "world");                // featuring "%s": fmtstr for strings
  printf("\thello,%s!", "world");                // featuring "\t": multiple tabs
  printf("\n\t\thello,%s!", "world");

  gotoxy(0,32); printf("\ihey %10s!",  "y'all"); // featuring "\i": write inverted
  gotoxy(0,24); printf("\ihey %-10s!", "y'all"); // even left-text-justify works!

  while (1);
}
the following is a demo program which shows how to write and scroll charts without having to calculate any (x,y) position to write to manually, in advance:

Code: Select all

task main() {
  int i;
  settabwidth(18);
  scrclr();

  for(;;++i) {       // (ascii char 0...31 not displayed)
    printf("\i%3d",i); printf(" %c",i); printf("\t\i%3d",++i); printf(" %c\n",i);
    if (_cur_y_<0) {
      curxy(0,8);  printf("\t\t\t\t <%d>\n", 1+i/16);
      printf("\t\t\t\t %s", "BTN");
      if (getchar()==BTNLEFT) {i=(i>=31?i-=32:111);}
      scrclr();
    }
    if (i>126) i=-1;
  }
}
Post Reply

Who is online

Users browsing this forum: No registered users and 2 guests