PBCOMMAND int PB_Fprint ( int type, const char* string, ...){
va_list args;
int ret;
va_start(args,string);
switch(type){
case STDOUT:
ret = vfprintf(stdout,string,args);
break;
case STDERR:
ret = vfprintf(stderr,string,args);
break;
default:
fprintf(stderr,"error! type %d not found",type);
return 0;
}
va_end(args);
return ret;
}
; Langage used to code the library: ASM or C
C
;
; Number of windows DLL than the library need
0
; Library type (Can be OBJ or LIB). Starting with 2.60, PureBasic can use both .obj (NAsm or LccWin32)
; and standard Windows .LIB file, useful to convert quickly a library. This should be mostly .obj
; here, instead you're really need to use a .lib.
;
LIB
;
; Number of PureBasic library needed by the library. Here we need the Gadget library
;
0
; Help directory name. Useful when doing an extension of a library and want to put
; the help file in the same directory than the base library. This is not a facultative
; result.
;
StdHandle
;
; Library functions:
; FunctionName, Arg1Type, Arg2Type, ... (DescriptionArg1, Arg2) - Description of the command show on the QuickHelp status bar
; Return type. Can be one of the following type: 'Long', 'Word', 'Byte', 'Float' 'String', 'None', 'InitFunction' (for a function
; which is called automatically at the start of a PureBasic program), 'EndFunction' (called at the end, for cleaning)
; and 'DebuggerCheck' which tell is there is a debugger handler function for this command.
;
Fprint,Long,String,[String],(Type.l,String$,[String$]) - Writes to stdout or stderr String$ using C formats
Long
Procedure main()
test.s = "test"
Fprint(#STDOUT, "This is a %s", test)
EndProcedure
main()
End
POLINK complaints about a missing _PB_Fprint2 function.
so may question is: How can i write a function to export in a pureLib with variable argument number
--:: Gkaizer ::--
Using PB 3.92 on WinXP SP2 and 3.91 on Mandrake Linux 10.0
CustomBox, String, [Long], [Long], (Title$ [, Flags [, Hidden]]) - My Custom box
Long
Now, you have to create 3 PB functions, named like this:
PB_CustomBox (char *String)
PB_CustomBox2(char *String, Long)
PB_CustomBox3(char *String, Long, Long)
Keep in mind that not only are the number of variables at issue, but the
type of variables are as well. You could assume that somebody would
use the variable in this sequence: string, string, long, but they may
want to use the sequence long, long, string. So the problem is not only
to recognize the number of variables, but to properly classify them as
to their type.
PowerBasic (another language I work with), reserves stack space for
each possible variable that might be passed. It has to impose certain
rules on which variables are allowed to be optional, and purges the
whole reserve on exit from the function or sub called, so to all
appearances, nuls were passed into the initial call when certain
variables are not used during the call itself.
Another approach is to pass a variable at the bottom of the stack (either
the first or last parameter, which instructs the called procedure as to
how many parameters were passed on the stack. If for instance, you
want to pass five parameters, the lowest variable value on the stack
should be a 0005. This would also be a flag as to how many 32-bit
values to remove from the stack after the call is complete.
But the problem is that not all parameters are the same type. And
not all parameters are optimized at 32-bits in length. So another
approach is to pass the number of variables on the stack as the first
(or lowest) value on the stack, say 0005 again (all parameters have to
be even multiples of number of bytes in length, so we cannot push just
a 05 (8 bits or 1 byte) onto the stack. The next word upwards on the
stack would then be a double flag: The upper byte would flag what
type of variable was being passed, and the lower byte would be a bite
count for the number of bytes that this variable requires. An example
might be this:
Now if we define the byte value represented by the -- as being the
total number of words passed on the stack, we would have an
easy method for determining how many words need to be purged
from the stack after the call is complete.
Makes sence, doesn't it? but it is not a standard. If it were a standard,
then you would see people writing compilers that would enforce these
rules. But you don't, and the only way you can make it happen is to
either follow the rules allowed by a given compiler, or devise your own
and create the calling and return processes in assembler code.
The trick would be to mate the assembler code back to a specific
compiler. Recognizing PowerBasic's approach, you would have to
define the maximum number of parameters that can be passed, and
they would have to be declared to be AS ANY to avoid type checking,
and presume that they are all 32-bits in length. You would have to
also insert integer flags at all the right positions that would serve to
instruct the assembler routine as to what type of reference was being
passed in the next parameter, with 4 bytes as the length. The first
parameter must be an integer to indicate the total number of words
passed on the stack and how many parameters are involved.
Of course, even if you got this to work, it doesn't save you anything
with PowerBasic - the number of flags and parameters could be very
large, setting all the flags correctly would have to be done manually
with no real advantage and a tendency to serious error in the process,
and the integers might have to be passed as 32-bit values and also
marked as OPT.
If you think about it, some languages like QBASIC, QuickBASIC,
GWBASIC, BASICA, PowerBASIC, and others give you a great
example of a variable calling process - the PRINT command. It seems
to allow you to print any number of elements, with or without semi-
colons, colons, tabs() embedded in the sequence. How does it do
this?
The answer is that it doesn't - the PRINT command handles each
element singularly. If I say PRINT " Now "; "is " "the " "time", timer,
then:
PrintString will print "Now "
The separating semicolon is ignored
PrintString will print "is "
The separating space is ignored
PrintString will print "the "
The separating space is ignored
PrintString will print "time "
PrintString will perform tab to the next (column MOD 8 = 1) column
PrintString will print a STR$(timer)
PrintString will perform tab to the next (column MOD 8 - 1) column.
It is the compiler or the interpreter that makes the decision as to
what next step to take when disecting a PRINT statement. It makes
it seem as though the Print command is some sort of super command,
but as you can see, it is just a matter of reducing the print statement
to a series of calls rather than one call.
And that is because the compiler or interpreter has a special process
that is unique to certain commands, such as Print and Write. So your
efforts to create a composite call similar to what you can do with
Print or Write are going to be doomed, because what you see is not
what is really going on underneath. Remember that though Print or
Write appear to be handling a variable number of variable types of
parameters, the source code is fixed at the time it is compiled or
interpreted. The compiler or interpreter can break it down into
individual steps to perform. And the reason it can do that is that
a print or write operation is a series of individual actions. What you
printed before and what you may print next has not bearing on the
effort to print something here and now.
Somebody raises the issue of, "yeah, but how about a statement like
MID(), where you can have optional parameters, such as the number
of characters to retrieve?" In that case, the compiler or interpreter
can have different versions of the same statement call: One without
a length specifier, and one with a link specifier. It just uses the call
version consistent with the code that appears in the source. Note
that the compiler or interpreter will probably still limit you as to what
type that optional parameter might be. If you declare it as an integer,
then you have to use an integer to call it. However, if the compiler
or interpreter doesn't care about maintaining the exact same type,
it might have versions of commands or statements that will accept
various types of parameters, such as a choice of byte, integer,
word, dword, long, float, or double. Or it may recognize the declared
parameter and the type pass by call are not consistent, and have its
own set of rules for converting one type into another before the call
is made. PureBasic can even convert numeric types to strings, or
vice versa, in some situations.
This is a rather lengthy breakdown of the problem, but maybe it will
help some people understand why what you can do within the source
code and what you can do in your own procedures just don't seem to
line up sometimes. Having procedures with variable parameters is
something of a reach. Having parameters of varying types used in
place of each other is even more difficult to manage.
has-been wanna-be (You may not agree with what I say, but it will make you think).