variable number of parameters passed to sscanf() at runtime

A

Andrew

Hi,

I have a scenario in which both the source and format string for a
sscanf() call is selected *at runtime*. The number of format
conversions/substitutions is known as well as the maximum length of
each, so it's easy enough to allocate the memory required dynamically.

The problem is, how do I call sscanf()?

The only way to acheive this I can think of is to mess with the stack
using embedded asm and call the function myself, but i'm unsure how I
could make this reasonably safe and portable. What I really need is a
function defined like this:

int asscanf(const char* source, const char* fmt, void** storage, int
storage_size);

where storage is an array of void* pointers for storing the converted
data.

Has anybody come up against this problem before or suggest anyway to
tackle it?

-- Andrew
 
V

Victor Bazarov

Andrew said:
I have a scenario in which both the source and format string for a
sscanf() call is selected *at runtime*.

An interpreter that contains printf-like (and scanf-like) functionality?
I can think of no other scenario that would require it, at this time.
The number of format
conversions/substitutions is known as well as the maximum length of
each, so it's easy enough to allocate the memory required dynamically.

The problem is, how do I call sscanf()?

The only way to acheive this I can think of is to mess with the stack
using embedded asm and call the function myself, but i'm unsure how I
could make this reasonably safe and portable. What I really need is a
function defined like this:

int asscanf(const char* source, const char* fmt, void** storage, int
storage_size);

where storage is an array of void* pointers for storing the converted
data.

Has anybody come up against this problem before or suggest anyway to
tackle it?


Yes, and the way to deal with it is to write your own "sscanf" using
string functions and istringstreams.

V
 
A

Andrew

The scenario is a simple but quite lengthy text protocol with messages
like so:

1 <formatted text>\n
2 <differently formatted text>\n
....

The first number is used to identify (amongst over things) the format
string used to extract further parameters from the rest of the string.
The idea being this will mute the need to do sscanf() (or other such
interpretation) calls in dozens of handler functions (there is atleast
1 for each message type).
 
E

Eric Jensen

Andrew said:
Hi,

I have a scenario in which both the source and format string for a
sscanf() call is selected *at runtime*. The number of format
conversions/substitutions is known as well as the maximum length of
each, so it's easy enough to allocate the memory required dynamically.

The problem is, how do I call sscanf()?

The only way to acheive this I can think of is to mess with the stack
using embedded asm and call the function myself, but i'm unsure how I
could make this reasonably safe and portable.

I'd say a asm block would be the shortest way to do this.
I made a very fast sample that compiles in vc++ 7 that
shows howto call a function inside a simple asm block.
You could with a asm block have a loop that pushes
the number of variables in your array onto the stack
and then calls sscanf, and afterwards pop the pushed
variables from the stack and continues.
You can do this with very little knowledge of assembly.

//eric

-- FILE stdafx.h --
#pragma once

#include <iostream>
#include <tchar.h>
-- EOF --
-- FILE asmtest.cpp --
#include "stdafx.h"
void testfunc(int i) {
std::cout << "void testfunc(int i): " << i << std::endl;
}
void testfunc2(int i, int j) {
std::cout << "void testfunc2(int i, int j): " << i << " " << j <<
std::endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
// calling with 1 arg
int i = 10, j = 20;
__asm {
PUSH i
CALL testfunc
POP i
}

// calling with 2 args
__asm {
PUSH j // arg 2 - read about calling convensions if you wonder why arg 2
is first ;)
PUSH i // arg 1
CALL testfunc2 // do the call
POP i // remove from stack
POP j // remove from stack
}
return 0;
}

-- EOF --
 
A

Andrew

I've started an implementation of "asscanf", it doesn't yet work (it's
not even called). My assembler is rusty and i've never actually done
any inline assembler in C before. Here it is, it compiles in GCC 3.4.5
on Linux (I'm hoping to port to Win32 later but this is all for today).

I would appreciate anybody steering me in the right direction if i'm
going just plain wrong, and thank you for the replies so far.

-- Andrew


#include <iostream>
#include <stdio.h>

__attribute__((cdecl))
void testfunc2(int j, int i) {
std::cout << "void testfunc2(int i, int j): " << i << " " << j <<

std::endl;
}

int asscanf(const char *str, const char *format,
const int num_ptrs, const void** ptrs)
{
int i;
int result;

// Need to add moving of the stack pointers (increasing and
// shrinking the stack)

for(int i = num_ptrs; i > 0; i--)
{ asm("push %0;" :: "m" (ptrs)); }

asm(
"push %0;\n\t" // format
"push %1;\n\t" // str
"call sscanf;"
: "=a" (result)
: "m" (format), "m" (str)
);

for(int i = num_ptrs; i > 0; i--)
{ asm("pop;"); } // Discard everything I dumped on the stack

return result;
}

int main(int argc, char* argv[])
{
int i = 10, j = 20;

sscanf("54", "%i", &j);

// calling with 2 args
asm("push %0;\n\t" // push i
"push %1;\n\t" // push j
"call _Z9testfunc2ii;\n\t" // call 'testfunc2'
"pop %1;\n\t" // pop j }
"pop %0;\n\t" // pop i } cleanup
:
: "m" (i), "m" (j)
:
);

return 0;
}
 
E

Eric Jensen

Andrew said:
I've started an implementation of "asscanf", it doesn't yet work (it's
not even called). My assembler is rusty and i've never actually done
any inline assembler in C before. Here it is, it compiles in GCC 3.4.5
on Linux (I'm hoping to port to Win32 later but this is all for today).
I would appreciate anybody steering me in the right direction if i'm
going just plain wrong, and thank you for the replies so far.

I never did any asm on linux or with gcc. However i just made the following
code for fun in vc++7 (win32). It uses printf to print out an int array.
Im posting it the hope that it can be any help to you.

//eric

/*
This code prints out the address in memory of each integer
in the array i[]. You can put as many integers into it as
you wish just remember to adjust the elements variable
*/
void PrintArrayAddrs(int *p, int s, int elements) {
/*
int *p - Pointer to last element in the array
int s - the size of a single element
int elements - the count of elements
*/
int c, h;
std::string strFormat;
strFormat = "Address of each variable in array:\n";
for (c=0, h=0; c<elements; c++, h++) {
strFormat += "%p ";
if (h == 7) {
strFormat += "\n";
h = 0;
}
}
const char *format = strFormat.c_str();
__asm {
mov eax,elements
mov ebx,p
PU_LOOP:
dec eax
push ebx
sub ebx,s
cmp eax,0
je _CALL
jmp PU_LOOP
_CALL:
push format
call printf
mov eax,elements
mov ebx,p
PO_LOOP:
dec eax
pop ebx
sub ebx,s
cmp eax,0
je DONE
jmp PO_LOOP
DONE:
pop format
}
}
int _tmain(int argc, _TCHAR* argv[])
{
int i[10] = {10,20,30,40,50,60,70,80,90,100}; // array to print addresses
of with printf
// this array could be floats or whatever just change the types
int elements = 10;
PrintArrayAddrs(&i[elements-1], sizeof(int),elements);
system("pause");
return 0;
}
 
E

Eric Jensen

for (c=0, h=0; c<elements; c++, h++) {
strFormat += "%p ";
if (h == 7) {
strFormat += "\n";
h = 0;
}
}

a little bug fix :)

if (h == 7) {
strFormat += "\n";
h = -1;
}
 
H

Heinz Ozwirk

Andrew said:
Hi,

I have a scenario in which both the source and format string for a
sscanf() call is selected *at runtime*. The number of format
conversions/substitutions is known as well as the maximum length of
each, so it's easy enough to allocate the memory required dynamically.

The problem is, how do I call sscanf()?

The only way to acheive this I can think of is to mess with the stack
using embedded asm and call the function myself, but i'm unsure how I
could make this reasonably safe and portable. What I really need is a
function defined like this:

int asscanf(const char* source, const char* fmt, void** storage, int
storage_size);

where storage is an array of void* pointers for storing the converted
data.

Has anybody come up against this problem before or suggest anyway to
tackle it?

Before you try something desparate, look for vsprintf.

HTH
Heinz
 
A

Andrew

the vsprintf and vsscanf family use macro's to enable the compiler to
generate machine code for calling functions with a variable but known
number of arguments at _compile time_. The number of arguments for any
specific call must be known at _compile time_. This is not what I am
after.

Or am I wrong??
 
M

Michiel.Salters

Andrew said:
Hi,

I have a scenario in which both the source and format string for a
sscanf() call is selected *at runtime*. The number of format
conversions/substitutions is known as well as the maximum length of
each, so it's easy enough to allocate the memory required dynamically.

The problem is, how do I call sscanf()?

Probably the best answer: Not. The second answer would probably be, in
a loop,
passing one argument at a time. for-loops can use a dynamical upper
bound ;)

Of course, since this is C++, someone will point out that streams will
make
it even simpler.

HTH,
Michiel Salters
 
J

Jonathan Mcdougall

Andrew said:
Hi,

I have a scenario in which both the source and format string for a
sscanf() call is selected *at runtime*. The number of format
conversions/substitutions is known as well as the maximum length of
each, so it's easy enough to allocate the memory required dynamically.

The problem is, how do I call sscanf()?

You don't, you use a stringstream:

# include <sstream>
# include <string>
# include <iostream>

int main()
{
std::string s = "just a 10 test";

std::string s1, s2, s3;
int i;

std::istringstream iss(s);
iss >> s1 >> s2 >> i >> s3;

std::cout << s1 << s2 << s3 << i;
}
The only way to acheive this I can think of is to mess with the stack
using embedded asm and call the function myself[...]
NooOOo!

int asscanf(const char* source, const char* fmt, void** storage, int
storage_size);

where storage is an array of void* pointers for storing the converted
data.

No no no no!

A combination of streams, containers, strings and perhaps boost::any
will solve your problem.


Jonathan
 
A

Alf P. Steinbach

* Andrew:
I have a scenario in which both the source and format string for a
sscanf() call is selected *at runtime*. The number of format
conversions/substitutions is known as well as the maximum length of
each, so it's easy enough to allocate the memory required dynamically.

The problem is, how do I call sscanf()?

The only way to acheive this I can think of is to mess with the stack
using embedded asm and call the function myself, but i'm unsure how I
could make this reasonably safe and portable. What I really need is a
function defined like this:

int asscanf(const char* source, const char* fmt, void** storage, int
storage_size);

where storage is an array of void* pointers for storing the converted
data.

Has anybody come up against this problem before or suggest anyway to
tackle it?

I don't see that there's any problem.

You'll need to parse the format string anyway.

So just scan one item at a time.
 
A

Andrew

You're right, I do need to tokenize the format string because the
formats are non-trivial. I'll reimplement a subset of the scanf
functionality using standard functions.
 
?

=?ISO-8859-1?Q?Martin_J=F8rgensen?=

Eric said:
"Andrew" <[email protected]> skrev i en meddelelse
-snip-
I would appreciate anybody steering me in the right direction if i'm
going just plain wrong, and thank you for the replies so far.


I never did any asm on linux or with gcc. However i just made the following
code for fun in vc++7 (win32). It uses printf to print out an int array.
Im posting it the hope that it can be any help to you.

//eric

/*
This code prints out the address in memory of each integer
in the array i[]. You can put as many integers into it as
you wish just remember to adjust the elements variable
*/
void PrintArrayAddrs(int *p, int s, int elements) {
/*
int *p - Pointer to last element in the array
int s - the size of a single element
int elements - the count of elements
*/
int c, h;
std::string strFormat;
strFormat = "Address of each variable in array:\n";
for (c=0, h=0; c<elements; c++, h++) {
strFormat += "%p ";
if (h == 7) {
strFormat += "\n";
h = 0;
}
}
const char *format = strFormat.c_str();
__asm {
mov eax,elements
mov ebx,p
PU_LOOP:
dec eax
push ebx
sub ebx,s
cmp eax,0
je _CALL
jmp PU_LOOP
_CALL:
push format
call printf
mov eax,elements
mov ebx,p
PO_LOOP:
dec eax
pop ebx
sub ebx,s
cmp eax,0
je DONE
jmp PO_LOOP
DONE:
pop format
}
}
int _tmain(int argc, _TCHAR* argv[])
{
int i[10] = {10,20,30,40,50,60,70,80,90,100}; // array to print addresses
of with printf
// this array could be floats or whatever just change the types
int elements = 10;
PrintArrayAddrs(&i[elements-1], sizeof(int),elements);
system("pause");
return 0;
}

How to make that work on g++?

g++ -fasm-blocks asm.c
asm.c: In function 'void PrintArrayAddrs(int*, int, int)':
asm.c:13: error: 'string' is not a member of 'std'
asm.c:13: error: expected `;' before 'strFormat'
asm.c:14: error: 'strFormat' was not declared in this scope
asm.c: At global scope:
asm.c:49: error: '_TCHAR' has not been declared
asm.c: In function 'int _tmain(int, int**)':
asm.c:52: error: 'of' was not declared in this scope
asm.c:52: error: expected `;' before 'with'
asm.c:55: error: 'elements' was not declared in this scope


Best regards / Med venlig hilsen
Martin Jørgensen
 
V

Victor Bazarov

Martin said:
Eric said:
"Andrew" <[email protected]> skrev i en meddelelse
-snip-
I would appreciate anybody steering me in the right direction if i'm
going just plain wrong, and thank you for the replies so far.


I never did any asm on linux or with gcc. However i just made the
following code for fun in vc++7 (win32). It uses printf to print out
an int array. Im posting it the hope that it can be any help to you.

//eric

/*
This code prints out the address in memory of each integer
in the array i[]. You can put as many integers into it as
you wish just remember to adjust the elements variable
*/
void PrintArrayAddrs(int *p, int s, int elements) {
[..]
__asm {
[..]
}
}
[..]

How to make that work on g++?

[..]

Built-in assembly is (a) platform-specific, (b) is defined to begin with
'asm' keyword, not '__asm' and (c) implementation-defined. If you need
a platform-specific compiler-specific solution, please consider taking
this conversation to a more appropriate newsgroup. Let's keep this about
C++ and not about Linux, Windows, GCC, VC++, etc.

V
 
J

Jonathan Mcdougall

Martin said:
Eric said:
"Andrew" <[email protected]> skrev i en meddelelse
-snip-
I would appreciate anybody steering me in the right direction if i'm
going just plain wrong, and thank you for the replies so far.


I never did any asm on linux or with gcc. However i just made the following
code for fun in vc++7 (win32). It uses printf to print out an int array.
Im posting it the hope that it can be any help to you.

//eric

/*
This code prints out the address in memory of each integer
in the array i[]. You can put as many integers into it as
you wish just remember to adjust the elements variable
*/
void PrintArrayAddrs(int *p, int s, int elements) {
/*
int *p - Pointer to last element in the array
int s - the size of a single element
int elements - the count of elements
*/
int c, h;
std::string strFormat;
strFormat = "Address of each variable in array:\n";
for (c=0, h=0; c<elements; c++, h++) {
strFormat += "%p ";
if (h == 7) {
strFormat += "\n";
h = 0;
}
}
const char *format = strFormat.c_str();
__asm {
mov eax,elements
mov ebx,p
PU_LOOP:
dec eax
push ebx
sub ebx,s
cmp eax,0
je _CALL
jmp PU_LOOP
_CALL:
push format
call printf
mov eax,elements
mov ebx,p
PO_LOOP:
dec eax
pop ebx
sub ebx,s
cmp eax,0
je DONE
jmp PO_LOOP
DONE:
pop format
}
}
int _tmain(int argc, _TCHAR* argv[])
{
int i[10] = {10,20,30,40,50,60,70,80,90,100}; // array to print addresses
of with printf
// this array could be floats or whatever just change the types
int elements = 10;
PrintArrayAddrs(&i[elements-1], sizeof(int),elements);
system("pause");
return 0;
}

How to make that work on g++?

How about searching a bit before asking?
g++ -fasm-blocks asm.c
asm.c: In function 'void PrintArrayAddrs(int*, int, int)':
asm.c:13: error: 'string' is not a member of 'std'
asm.c:13: error: expected `;' before 'strFormat'
asm.c:14: error: 'strFormat' was not declared in this scope

# include said:
asm.c: At global scope:
asm.c:49: error: '_TCHAR' has not been declared

_TCHAR doesn't exist in standard C++. Make it a const char*.
asm.c: In function 'int _tmain(int, int**)':

int main()
asm.c:52: error: 'of' was not declared in this scope
asm.c:52: error: expected `;' before 'with'

Seriously?? The comment is on two lines! Wake up!
asm.c:55: error: 'elements' was not declared in this scope

Probably because of the other errors.


Jonathan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top