allow variable arguments

Got an idea for enhancing PureBasic? New command(s) you'd like to see?
xorc1zt
Enthusiast
Enthusiast
Posts: 276
Joined: Sat Jul 09, 2011 7:57 am

allow variable arguments

Post by xorc1zt »

hi

Please could you add a keyword to disable the check of parameters number like "..." with C
or a option to disable the "incorrect number of parameters" error. I highly need dat.
User avatar
STARGÅTE
Addict
Addict
Posts: 2227
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: allow variable arguments

Post by STARGÅTE »

And how you want access this undefined (name and type) parameters?
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: allow variable arguments

Post by ts-soft »

I think this will never come, but ...?

Here is a workaround: http://www.purebasic.fr/english/viewtop ... 80#p376680
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
xorc1zt
Enthusiast
Enthusiast
Posts: 276
Joined: Sat Jul 09, 2011 7:57 am

Re: allow variable arguments

Post by xorc1zt »

STARGÅTE wrote:And how you want access this undefined (name and type) parameters?
you must pick the memory between the first parameters and the first variable. here is a example

Code: Select all

procedure printf ( *text, ... )
	define *paramlist_start
	define *paramlist_end
	*paramlist_start = @*text + sizeof( int ) ; get the stack address of the undefined parameters
	*paramlist_end = @*paramlist_start - *paramlist_start
then you increment dependently of the type requested.

Code: Select all

; %d or %i
arg = peeki( *paramlist_start )
*paramlist_start + sizeof( int ) ; going to the next argument

; %c
arg = peekc( *paramlist_start )
*paramlist_start + sizeof( char ) ; going to the next argument

...

; more request than arguments ?
if( *paramlist_start > *paramlist_end )
    debug "overflow"
endif
you could avoid the incrementation if you force a alignment but it will be less flexible.
ts-soft wrote:I think this will never come, but ...?

Here is a workaround: http://www.purebasic.fr/english/viewtop ... 80#p376680
the main purpose of my request is to be able to import C function which use var args, this is not hard to implement just disable the
error check if the last defined argument is "..." (c style) or maybe a new type/keyword ?





EDIT: more about

http://www.cplusplus.com/reference/clibrary/cstdarg/

from microsoft libc

stdarg.h:

Code: Select all

/***
*stdarg.h - defines ANSI-style macros for variable argument functions
*
*       Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose:
*       This file defines ANSI-style macros for accessing arguments
*       of functions which take a variable number of arguments.
*       [ANSI]
*
*       [Public]
*
****/

#pragma once

#ifndef _INC_STDARG
#define _INC_STDARG

#if     !defined(_WIN32)
#error ERROR: Only Win32 target supported!
#endif


#include <vadefs.h>

#define va_start _crt_va_start
#define va_arg _crt_va_arg
#define va_end _crt_va_end

#endif  /* _INC_STDARG */

vadefs.h:

Code: Select all

/***
*vadefs.h - defines helper macros for stdarg.h
*
*       Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose:
*       This is a helper file for stdarg.h
*
*       [Public]
*
****/

#pragma once

#ifndef _INC_VADEFS
#define _INC_VADEFS

#if     !defined(_WIN32)
#error ERROR: Only Win32 target supported!
#endif


/*
 * Currently, all MS C compilers for Win32 platforms default to 8 byte
 * alignment.
 */
#undef _CRT_PACKING
#define _CRT_PACKING 8
#pragma pack(push,_CRT_PACKING)

#ifdef  __cplusplus
extern "C" {
#endif


#if !defined(_W64)
#if !defined(__midl) && (defined(_X86_) || defined(_M_IX86))
#define _W64 __w64
#else
#define _W64
#endif
#endif

#ifndef _UINTPTR_T_DEFINED
#ifdef  _WIN64
typedef unsigned __int64    uintptr_t;
#else
typedef _W64 unsigned int   uintptr_t;
#endif
#define _UINTPTR_T_DEFINED
#endif

#ifndef _VA_LIST_DEFINED
#ifdef _M_CEE_PURE
typedef System::ArgIterator va_list;
#else
typedef char *  va_list;
#endif /* _M_CEE_PURE */
#define _VA_LIST_DEFINED
#endif

#ifdef  __cplusplus
#define _ADDRESSOF(v)   ( &reinterpret_cast<const char &>(v) )
#else
#define _ADDRESSOF(v)   ( &(v) )
#endif

#if     defined(_M_IA64) && !defined(_M_CEE_PURE)
#define _VA_ALIGN       8
#define _SLOTSIZEOF(t)   ( (sizeof(t) + _VA_ALIGN - 1) & ~(_VA_ALIGN - 1) )

#define _VA_STRUCT_ALIGN  16 

#define _ALIGNOF(ap) ((((ap)+_VA_STRUCT_ALIGN - 1) & ~(_VA_STRUCT_ALIGN -1)) \
        - (ap))
#define _APALIGN(t,ap)  (__alignof(t) > 8 ? _ALIGNOF((uintptr_t) ap) : 0)

#else
#define _SLOTSIZEOF(t)   (sizeof(t))
#define _APALIGN(t,ap)  (__alignof(t))
#endif

#if     defined(_M_CEE)

extern void __cdecl __va_start(va_list*, ...);
extern void * __cdecl __va_arg(va_list*, ...);
extern void __cdecl __va_end(va_list*);

#define _crt_va_start(ap,v)  ( __va_start(&ap, _ADDRESSOF(v), _SLOTSIZEOF(v), \
                                __alignof(v), _ADDRESSOF(v)) )
#define _crt_va_arg(ap,t)    ( *(t *)__va_arg(&ap, _SLOTSIZEOF(t), \
                                _APALIGN(t,ap), (t *)0) )
#define _crt_va_end(ap)      ( __va_end(&ap) )

#elif   defined(_M_IX86)

#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

#define _crt_va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _crt_va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define _crt_va_end(ap)      ( ap = (va_list)0 )

#elif defined(_M_IA64)

#ifdef  __cplusplus
extern void __cdecl __va_start(va_list*, ...);
#define _crt_va_start(ap,v)  ( __va_start(&ap, _ADDRESSOF(v), _SLOTSIZEOF(v), \
                          _ADDRESSOF(v)) )
#else
#define _crt_va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) + _SLOTSIZEOF(v) )
#endif

#define _crt_va_arg(ap,t)    (*(t *)((ap += _SLOTSIZEOF(t)+ _APALIGN(t,ap)) \
                                                     -_SLOTSIZEOF(t)))

#define _crt_va_end(ap)      ( ap = (va_list)0 )

#elif defined(_M_AMD64)


extern void __cdecl __va_start(va_list *, ...);

#define _crt_va_start(ap, x) ( __va_start(&ap, x) )
#define _crt_va_arg(ap, t)   \
    ( ( sizeof(t) > sizeof(__int64) || ( sizeof(t) & (sizeof(t) - 1) ) != 0 ) \
        ? **(t **)( ( ap += sizeof(__int64) ) - sizeof(__int64) ) \
        :  *(t  *)( ( ap += sizeof(__int64) ) - sizeof(__int64) ) )
#define _crt_va_end(ap)      ( ap = (va_list)0 )

#else

/* A guess at the proper definitions for other platforms */

#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

#define _crt_va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _crt_va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define _crt_va_end(ap)      ( ap = (va_list)0 )

#endif

#ifdef  __cplusplus
}
#endif

#pragma pack(pop)

#endif  /* _INC_VADEFS */
 
User avatar
idle
Always Here
Always Here
Posts: 5844
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: allow variable arguments

Post by idle »

but wouldn't this imply that it's taking a quad?
/*
* Currently, all MS C compilers for Win32 platforms default to 8 byte
* alignment.
*/
Windows 11, Manjaro, Raspberry Pi OS
Image
xorc1zt
Enthusiast
Enthusiast
Posts: 276
Joined: Sat Jul 09, 2011 7:57 am

Re: allow variable arguments

Post by xorc1zt »

dunno about this alignment i just tried on vc2010 x86 and there is no 8b alignment by default

to check i did this

Code: Select all

#include <stdio.h>
#include <tchar.h>

void teststackalignment(int a, short b, __int8 c, __int64 d, float e, char f)
{
	printf("stack alignment:\na at %0x\n", &a);
	printf("b at %0x (+%i)\n", &b, (int)&b-(int)&a);
	printf("c at %0x (+%i)\n", &c, (int)&c-(int)&b);
	printf("d at %0x (+%i)\n", &d, (int)&d-(int)&c);
	printf("e at %0x (+%i)\n", &e, (int)&e-(int)&d);
	printf("f at %0x (+%i)\n", &f, (int)&f-(int)&e);
}

struct testalign {
	int a;
	short b;
	__int8 c;
	__int64 d;
	float e;
	char f;
};

int _tmain(int argc, _TCHAR* argv[])
{
	struct testalign strtest;
	printf("structure alignment:\na at %0x\n", &strtest.a);
	printf("b at %0x (+%i)\n", &strtest.b, (int)&strtest.b-(int)&strtest.a);
	printf("c at %0x (+%i)\n", &strtest.c, (int)&strtest.c-(int)&strtest.b);
	printf("d at %0x (+%i)\n", &strtest.d, (int)&strtest.d-(int)&strtest.c);
	printf("e at %0x (+%i)\n", &strtest.e, (int)&strtest.e-(int)&strtest.d);
	printf("f at %0x (+%i)\n------------\n", &strtest.f, (int)&strtest.f-(int)&strtest.e);

	teststackalignment(55,(short)88,(__int8)678,(__int64)487,85.5f,85);
	printf("\n_CRT_PACKING = %i\n",_CRT_PACKING);

	system("PAUSE");
	return 0;
}
produce
structure alignment:
a at 41fec4
b at 41fec8 (+4)
c at 41feca (+2)
d at 41fecc (+2)
e at 41fed4 (+8)
f at 41fed8 (+4)
------------
stack alignment:
a at 41fdd8
b at 41fddc (+4)
c at 41fde0 (+4)
d at 41fde4 (+4)
e at 41fdec (+8)
f at 41fdf0 (+4)

_CRT_PACKING = 8
Press any key to continue . . .
DontTalkToMe
Enthusiast
Enthusiast
Posts: 334
Joined: Mon Feb 04, 2013 5:28 pm

Re: allow variable arguments

Post by DontTalkToMe »

+ 1, as someone would say

Currently we can import most C functions at compile time with ImportC and dynamically with PrototypeC + GetFunction().

Would be useful to have the ability to properly import C functions like printf().
Many C libraries export functions like that.

PB could use something like "..." as the last parameter of a function prototype/import to indicate a variable number of parameters.

When generating the code, the compiler could check how many parameters the caller is using and simply push that number of parameters on the stack, to remove them later.

It shouldn't be a problem to implement this one.

At the moment we must use multiple prototype definitions, each one with a different number of parameters, and invoke a different function each time as needed. Not nice.

Or we can use CallCFunctionFast(). This one supports a variable number of parameters but it's not compatible with prototypes or ImportC.

To have the ability to specify a variable number of parameters for PB procedures would be a plus, practically mixing cdecl and stdcall conventions as needed.

But even just the importing bit would be a nice step, after all one of the cardinal features of cdecl is the ability to specify a variable number of parameters, so if you implement an ImportC and a PrototypeC how could you not support that ?
User avatar
Tenaja
Addict
Addict
Posts: 1959
Joined: Tue Nov 09, 2010 10:15 pm

Re: allow variable arguments

Post by Tenaja »

Don't hold your breath. The way PB currently handles local variables would force a rewrite of its local-variable handling. Instead of using a Frame Pointer (as required for variable arguments), PB references directly from the stack. This leaves an extra register for other uses, but means you cannot push or pop the stack without corrupting the offset reference (as required for variable arguments).

...maybe when the llvm port is implemented? (2020?)
Post Reply