开发者

What's the best way to rewrite this generic function I wrote in C++ in C?

//Prints out a given array 
template <typename T>
void print(T t)
{
    for(int i = 0; i < t.size(); i++)
    {
        cout << t[i] << " ";
    }
    cout << endl;
}

I have an idea but it includes passing the 开发者_StackOverflowsize of the array. Is it possible to avoid this?

*Update Thanks for all of the answers/ideas but this problem is getting way deeper than my snorkeler can handle. I wanted to rewrite my C++ code in C because it was horribly written and slow. I see now that I have an opportunity to make it even worse in C. I'll rewrite it from the ground up in Python(performance be damned). Thanks again


If you don't have ELEMENTS, it's

#define ELEMENTS(a) (sizeof(a)/sizeof(*a))

Then,

#define print_array(a, specifier) print_array_impl(a, specifier, ELEMENTS(a), sizeof(*a))

void print_array_impl(void* a, char* specifier, size_t asize, size_t elsize)
{
    for(int i = 0; i < asize; i++)
    {
        // corrected based on comment -- unfortunately, not as general
        if (strcmp(specifier, "%d") == 0)
           printf(specifier, ((int*)a)[i]); 
        // else if ... // check other specifiers
        printf(" ");
    }
    printf("\n");
}

Use like this

print_array(a, "%d") // if a is a int[]

and, a needs to be an array name, not a pointer (or else ELEMENTS won't work)


You cannot know what is the size of an array without passing the size of that array (except operating with sizeof in static arrays). This is because the a pointer to a block of memory will only point to the base of the block of memory, from which you can know where the array/block of memory starts, but as there is no end defined you cannot determine where it will end.

  • You either need to set your own length per array and preserve it, and use it with the array like as described:

    • You can make a new type like:
    struct _my_array {
       typename arr[MAX];
       int n;
    } my_array;

    OR

    struct _my_array {
       typename *arr;
       int n;
    } my_array;

    In this case you need to allocate the a block of memory dynamically with new or malloc , and when finished free the memory with delete or free (respectively).

    • Or you can simply pass the array number of elements through the function.
  • Another way is to use a special terminator value of your array type which if encountered will be determined as the end of the array. In this case you need not preserve the size. For example a string is '\0' terminated, so all the string functions know that when a '\0' character is encounter in the char array it will consider that the string has end.

UPDATE

Because this is a generic function and the array can be of any type, one thing which you can do is like this:

struct _my_generic_arr {
  void *arr;
  int n;
  int type;
} my_generic_arr;

When populating this array you can use any type. To identify which type, pass an identified in the type component. Each unique value will determine which type does the arr pointer actually points to (was actually the intended type to be pointed). The n will define the length. Now, depending on different values of type make a switch - case or an if - else ladder or nest, and process the array as you need.


It is impossible in c to track the size of an array in other block,, It would be a better option to pass the size of the array along..

The other option would be to declare a global variable that has the size and using that variable inside the function Eg,,

int size=<some value>
void main()
{
int arr[<same value>];
}
void print(T t)
{
    for(int i = 0; i < size; i++)
    {
       printf("%d ",t[i])   //assuming T as int
    }
   printf("\n");
}


In C, you would need to pass two additional parameters: the size of the array (as you mentioned), and some way of indicating how to convert t[i] into a string. To convert t[i] to a string, you could create a custom switch statement to decode possible types, pass a pointer to a function that will return the string pointer, or you could pass the printf format specifier (e.g. "%d" for integer).


The problem is larger than you think. If you have an array of size 12, how do you know what data is held in that array? It could be 3 char*'s (on 32 bit system), 3 int32_t's, or even 12 chars. You have no way of knowing how to interpret the data. The best you could do is to implement your own version of a v-table and putting a print or toString function into it.


typedef struct {
    void *array;
    size_t length;
    int element_width;
    printer_t to_string;
} container;

printer_t is a type that describes a function pointer that takes an element pointer and returns a string (or prints it, if you don't want to free the string). This is almost never worth doing in C. That doesn't mean it can't be done. I would emphasize, though, that none of this is intended to imply that it should be done.

The function itself would look something like this:

void print(container *thing)
{
    size_t offset;
    int width;
    char *stringified;

    width = thing->element_width;
    for (offset = 0; offset * width < thing->length; offset += width)
    {
        stringified = thing->to_string(thing->array + offset);
        printf("%s ", stringified);
        free(stringified);
    }
}

What this does is essentially turn a struct into a faux class with a function pointer for a method. You could be more object-oriented and put the method in the type being printed and make it an array of those instead. Either way, it's not a good idea. C is for writing C. If you try to write in a different language, you'll end up with all sorts of terrible stuff like this.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜