3

Mostly for fun, I've decided to write my own minimal test framework for my C code. I use a basic struct for the test information, create an array of test structs and then iterate over them to run all the tests. This amounts to a very small amount of work for a fairly elegant (imho) solution.

However, the one thing that is a little annoying is that I cannot figure out how to define functions as function pointers instead of defining the function and then creating a function pointer later.

I have the following (which works just fine):

typedef int (* test_p) (void);

struct test {
    char * desc;
    test_p func;
};

int
example_test (void) {
    puts("This is a test");
    return 0;
}

void
run_test (char * test_name, test_p test) {
    printf("Testing %s\t\t\t[ PEND ]\r", test_name);
    char * test_result = (test() ? "FAIL" : "PASS");
    printf("Testing %s\t\t\t[ %s ]\n", test_name, test_result);
}

int
main (void) {
    struct test test_list [] = {
        { "example test", (test_p )example_test }
    };

    for ( int i = 0; i < 1; i ++ ) {
        run_test(test_list[i].desc, test_list[i].func);
    }

    return 0;
}

However, I am hoping I can remove the need for the casting in the struct and instead define the function as being a function pointer from the beginning. The following is an example of how I would like this to work (assuming many of the same things as above):

test_p
example_test = {
    puts("This is a test");
    return 0;
}

If I could do something like this, then in the struct, I could simply have the func field be example_test rather than (test_p )example_test. Is this (or something like it) possible? If not, is there a reason why not (If that reason is simply "because it wasn't added to the language", that's fine)?

13
  • 5
    Remove the need for what casting? You mean the unneeded casting ? See it live. You want to store a function address and later invoke it, a function pointer is the way to do that. The alternate proposed syntax make no sense and is not how the language works. Commented Aug 11, 2014 at 2:39
  • 1
    @FiddlingBits: Wouldn't that need a parameter list? Commented Aug 11, 2014 at 2:50
  • 1
    test_p example_test(void) { puts("This is a test"); return 0; }. Just one small step remains, replace test_p with int and you are back at square one, which you shouldn't have left in the first place. There is no alternative syntax to define functions in C. Commented Aug 11, 2014 at 3:59
  • 1
    @FiddlingBits: note that you have defined a function that returns a pointer to a function that returns int and takes no arguments; this is quite radically different from a function that returns int and takes no arguments. You can't use a function pointer typedef (or a function typedef) to define a function of the type implied by the pointer type. You have to create the function as a regular function and then use the name as a pointer to function. Commented Aug 11, 2014 at 4:23
  • 1
    @HalosGhost: you simply can't create a function using the notation you are seeking to use. It is not C. It never was C; it is fairly unlikely ever to be C — but stating absolutely that it never will be C requires greater prescience than I claim to possess. (See Clarke's Three Laws: When a distinguished but elderly scientist states that something is possible, he is almost certainly right. When he states that something is impossible, he is very probably wrong. I don't claim to be distinguished or a scientist, either). Commented Aug 11, 2014 at 4:27

2 Answers 2

3

A function pointer is one kind of thing and a function is another kind of thing so you can't really make the latter be the former. But if you use a function name where a function pointer is expected, that produces a pointer to the function, so you can just remove the unnecessary cast, as WhozCraig said in the first comment above. You write

If I could do something like this, then in the struct, I could simply have the func field be example_test rather than (test_p )example_test.

You can do that, with example_test defined just as it is in your current code ... did you try that?

You can also forward declare a function, like so:

typedef int test_func(void); // note no indirection
typedef test_func* test_p;

test_func example_test;

It would be nice if you could use that sort of syntax when you define the function, as in your attempted syntax, but there's simply no way to do that in C ... you have to explicitly provide the return type and parameter list.

Another detail is that, when you invoke the function pointed to by a function pointer, you don't have to dereference it ... that's why you were able to write

test()

instead of

(*test)()

although the latter also works. (In fact, because the deference is stripped, (********test)() also works ... but only do that if you're trying to win an obfuscation contest.)

Sign up to request clarification or add additional context in comments.

Comments

1

What you are describing is a kind of meta-programming. Rather than writing code to explicitly solve the problem, you are concerned with a kind of syntactic structure that will allow you to define a whole raft of test functions without unnecessary cruft.

In Lisp you would use macros. In C++ you might use templates and/or lambdas. In C you use macros.

So you need to write a macro that:

  • takes a name and descriptive text as arguments
  • defines a static variable of type function (created from that name using token pasting)
  • defines a function (using a name created by token pasting)

[edit] At this point you have achieved the goal: you have created the function and given it a name that is (only) a function pointer, and you can use that name in your struct without a cast. I would suggest one additional step, the macro also:

  • adds the variable/function and descriptive text to a list of functions to be tested.

Then your boilerplate loop iterates over the structure calling each function and reporting the results using the descriptive text. Problem solved.

Some people don't like macros, but they are ideally suited to this situation, and there is no other way to do it in C. I did something just like this before making the move to C++.

1 Comment

A good answer, but a shame that it doesn't answer the question at all.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.