Fog Creek Software
Discussion Board




Another C question.

Let's say you have a function like this:

void showStarAtPoints(
    star_t *star,
    int total_points,
    ...
);

And you call it like this:

showStarAtPoints(star, 3, 23.78, 45.23, 89.75);

Where "3" is the total number of points for showStarAtPoints() to expect, followed by the points themselves. Now lets pretend this function draws the specified star at those three points with lines connecting each star at each of the three points.

Now lets say I have an array of points that I need to somehow supply to this function--how could I do it? Is this remotely possible?

Covance does *NOT* like animals
Thursday, June 24, 2004


It's impossible. Switch to C#

A Pterodactylus Ate My Baby
Thursday, June 24, 2004

errr.... pointers?

muppet from forums.madebymonkeys.net
Thursday, June 24, 2004

You can do it but you need to use a special-purpose library like libffi (foreign function interface) which is a set of platform-specific assembly language routines that builds a function call at run time. It's unfortunate that C included varargs as a standard feature but omitted the ability to *call* functions with runtime arguments.

BTW this doesn't have anything to do with my StarPro software, does it? :)

Dan Maas
Thursday, June 24, 2004

No, it's not remotely possible. But I think you would be better off switching to Visual Basic than to C#.

Ian
Thursday, June 24, 2004

Forget those idiots above. Trust me, it's impossible.

Ian
Thursday, June 24, 2004

now I understand why I never bothered much with C.

What a ridiculous, antiquated language.

muppet from forums.madebymonkeys.net
Thursday, June 24, 2004

'C' certainly has multi-dimensioned arrays.  Brute force you can do it that way.

A better way would be to define a typedef 'position' with X,Y,and Z float members:

typedef struct
{
  X : float;
  Y : float;
  Z : float;
} PositionType;

Now you can have an array of these things, and pass a pointer to the array:

MyStars : PositionType[6];

What's the problem here?

 

AllanL5
Thursday, June 24, 2004

Problem is he wants to populate the variable argument list with the array at run-time.  I remember wanting to do something similar once.  Could not be done.  Its like the invese of var args.  You could do it in assembler though, move the stack pointer and fill in the stack with elements of the array.

hoser
Thursday, June 24, 2004

You have a fundamental design problem and a flawed understanding of pointers.  If each star is located at a specific location then that location should be part of the star_t struct otherwise you would pass in a pointer to an array of point_t's describing the location of each respective star.

Remember a pointer can be used to access an array.  This is a common use of a pointer.

struct star_t
{
  int x;
  int y;
  COLORREF color;
}

/* allocate 10 stars */
star_t *star = (star_t *)malloc(10 * sizeof(star_t));
memset((void *)star, 0x00, 10 * sizeof(start_t));

/* modify the first element in the array */

star[0].x = 1;
star[0].y = 1;
star[0].color = RGB(0, 0, 0);

/* point to the next element of the array - pointer is incremented by the size of the type */

star++;

/* modify the second element in the array */

star->x = 1;
star->y = 1;
star->color = RGB(0, 0, 0);

TR
Thursday, June 24, 2004

Read the question.

hoser
Thursday, June 24, 2004

In C there is almost always a way, if you are willing to make some assumptions about how the compiler is implemented, and then do a lot of dirty, dirty code.  I almost think you could take the address of the function, cast it's type so that the vararg part is an array of some fixed size, and then call it through the mistyped interface.  As long as everything on the stack was where it was expected (which may be reverse position from what you though it would be) things magically work.  Of course, I haven't tried it, so I could be wrong.

Really, the short answer is "C does not want you to do that".

Keith Wright
Thursday, June 24, 2004

I did read the question.  For each star you would have allocated a certain number of points.

struct {
  float x;
  float y;
  float z;
} point_t;

struct {
  point_t *points;
  int count;
} star_t;

A fundamental data structure problem and a misunderstanding of pointers.

TR
Thursday, June 24, 2004

If the array you need to pass is of a fixed size or reasonably bounded you could generate new functions for each possible array size that would blow the array up into arguments to the vararg function.  A master function that could take the array size and array then call the appropriate function could be used to provide a single entry point.  Ugly but portable and the code would be easy to machine generate to just about any array size depending on what the compiler could handle.

Doug
Thursday, June 24, 2004

I like keith's answer +1

christopher baus (www.baus.net)
Friday, June 25, 2004

This isn't a case of "C is a bad language," or "not understanding pointers." It's a design problem: every such function should have a counterpart that takes a fixed number of arguments (in this case, a pointer to an array of positions). Compare sprintf and vsprintf, etc.

The question should not be "how do I?", but "why should I have to?". Complain to the function writer.

scruffie
Friday, June 25, 2004

why not simply using a linked list ?

Should do the job. google for this.. you may find an answer.

bernhard
Friday, June 25, 2004

Yes, it can be done. Look at the printf function: it does exactly that. Hasn't anyone heard of variable argument lists?

Look up stdarg; in stdarg.h are some macros for working with variable argument lists (va_start, va_arg, va_end, va_copy). I have to admit that I never used them since I never needed them, but they are there for anyone who needs them.

vrt3
Friday, June 25, 2004

Wrap the whole argument list for one item in a struct. Pass an array of those structs. If necessary (e.g. if this is in an immutable library) create a wrapper function which calls the lib function as many times as necessary.


Friday, June 25, 2004

Jesus.  No wonder programming work is being outsourced to India.

Jorel on Software
Friday, June 25, 2004

And next, someone is going to come up and say half those posters were wrong, although with good intentions.

RP
Friday, June 25, 2004

Do you want to bet the salad cream on it?


Friday, June 25, 2004

vrt3, you misunderstood the question...

and OP, it's a design flaw in the original code. Try to keep away from variable argument functions as much as possible. Try using a linked list (or if you use C++ and just stated C, use the STL list<start_t*>) or an array instead, since you pass the size argument anyway. A much cleaner and more efficient way of doing it!

Anyway, seems your original question has been answered (there's no clean way to do it in C, no)

Marc
Friday, June 25, 2004

Ah yes, I misunderstood. Sorry about that.

vrt3
Friday, June 25, 2004

Aha, the original question was how to use VARARGS to implement a routine which could take a variable number of star points as its parameter.

And all the answers were basically correct.

1.  Don't DO That.  -- It is not a good idea to code this way.  It becomes very hard to impossible to test.  (Ok, it works with one point, two points, three points... when do you stop?).  Yes, 'printf' works this way.  There are certain very limited tasks where this approach makes sense.  This is not one of them, there are LOTS of much better alternative approaches.

2.  'C' won't do that -- well, jumping through very dirty VarArg hoops, it COULD -- but you still don't want to do it that way.  See #1.

3.  Who gave you this task in the first place, and WHY are they insisting on this approach?  If this approach was your idea, do something else.  If this approach was their idea, try to get them to change their mind.

4.  Make an array of star points (that can EASILY be of variable length in 'C') and pass that.  This is the canonical way 'C' programmers do this sort of thing.

5.  Use a better structure than an 'Array'.  Pass that.

AllanL5
Friday, June 25, 2004

Off topic...

Hey, OP, I used to work for Covance.  (We didn't do any animal testing at our location, incidentally.)  I can tell you some fun stories about their "database" system if you like.

They're not real fond of their employees, either.

Aaron F Stanton
Friday, June 25, 2004

Hi. Thanks for the replies everyone.

The function in my original post was hypothetical, but it way modeled after a very real function I'm working on now. The real function belongs to a group of other functions that generate output for a particular text format. Each function generates a particular field, and this one field handled by this function was made up of pairs of two different numbers, so the function had to handle multiple numbers of alternating types, one after the other.

I guess I'll have to create a version of the function that takes a linked list where each member contains the two numbers and links to the next item instead.

Covance does *NOT* like animals
Friday, June 25, 2004

Ofcourse it can be done in C/C++.

I cannot believe that no one thought to suggest this simple solution. And yes I have tested it...

#include <stdio.h>
#include <stdarg.h>

void showStarAtPoints ( void *star, int total_points, ...) ;

int main(int argc, char* argv[])
{
    showStarAtPoints ( 0, 3, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 ) ;
    showStarAtPoints ( 0, 2, 11.0, 12.0, 13.0, 14.0 ) ;
    showStarAtPoints ( 0, 0 ) ;
    showStarAtPoints ( 0, 1, 1000.11, 1000.12 ) ;
    return 0;
}

void showStarAtPoints ( void *star, int total_points, ...)
{
    va_list pointstart ;

    va_start ( pointstart, total_points ) ;

    printf ( "Dumping data for %d points\n", total_points ) ;
    for ( int i = 0; i < total_points; i++ )
    {
        double d1 = va_arg ( pointstart, double ) ;
        double d2 = va_arg ( pointstart, double ) ;
        printf ( "x=%g, y=%g\n" , (float) d1, d2 ) ;
    }

    va_end ( pointstart ) ;             
}

Code Monkey
Friday, June 25, 2004

Code Monkey, did you read the rest of my question? Here:

"Now lets say I have an array of points that I need to somehow supply to this function--how could I do it?"

Just creating the stdarg function isn't the problem, it's how to supply the arguments to it dynimcally, like from an array, that's the problem.

Covance does *NOT* like animals
Friday, June 25, 2004

Wow 'Monkey, thanks for a nice clean 'Var_Arg' implementation.  You even included the 'stdarg.h' include that lets it all work.  You da' man.

I still think the linked list of structures is a more robust and general solution, but 'Monkey has given you the canonical 'C' 'Var_Arg' implementation.  Cool.

AllanL5
Friday, June 25, 2004

If you have the option to *design* the star-making function, then by all means use an array of structs, or even an iterator object. (the simplest version being a pointer to a function that gets the next star).

And I DID answer the question correctly :). If you want to construct a call to a varargs function at run-time, you have to use ABI-specific assembly code. It's not impossible, but it's not pretty either.

Dan Maas
Friday, June 25, 2004

Wow, everyone is overcomplicating this. Just create another function:

void showStarAtPoints(
    star_t *star,
    int total_points,
    float *x,
    float *y
);

where x and y are your arrays for the positions (or create a struct combining these if you like).

Jim Buck
Friday, June 25, 2004

Yes, its possible. But its platform dep assembly.


Basically you setup the stack just like a var args call.
And then call it.

I guess that technically not C though is it?

Bah!

B
Friday, June 25, 2004

*  Recent Topics

*  Fog Creek Home