Path: csiph.com!eternal-september.org!feeder.eternal-september.org!reader01.eternal-september.org!.POSTED!not-for-mail From: Tim Rentsch Newsgroups: comp.lang.c Subject: Re: Abstract Syntax Tree for C programs Date: Tue, 04 Jun 2019 10:41:17 -0700 Organization: A noiseless patient Spider Lines: 102 Message-ID: <86h895fdtu.fsf@mailhub.linuxsc.com> References: <475067fe-369c-4246-84a1-05b1a3a0c6be@googlegroups.com> <5dd4672f-a571-40bc-bb1a-c14f6c631eb7@googlegroups.com> <87ftop38c1.fsf@bsb.me.uk> <87woi11jh1.fsf@bsb.me.uk> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Injection-Info: reader02.eternal-september.org; posting-host="822064cc3cc2a6ececc8a34836f7fa7b"; logging-data="11032"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX19XDRxrf+HB3CfpwH/WINHT2kUVEmkGe6g=" User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.4 (gnu/linux) Cancel-Lock: sha1:zaoAmt15QiLBsEZD33GOc1i/6LY= sha1:A+xGRqpffGIknVi2NeNv/6t3BHA= Xref: csiph.com comp.lang.c:144417 Ben Bacarisse writes: > David Brown writes: > >> On 04/06/2019 13:20, Ben Bacarisse wrote: >> >>> Bart writes: >>> >>>> On 04/06/2019 07:56, Ike Naar wrote: >>>> >>>>> On 2019-06-03, Bart wrote: >>>>> >>>>>> Using a void* to store a void(*)(void) is not a big deal, you >>>>>> don't want -pedantic-errors to stop your build because of it. >>>>> >>>>> Using a function pointer (and not an object pointer) to store a >>>>> function pointer isn't a big deal either. >>>> >>>> How would you create the following table: >>>> >>>> void fn1(void){} >>>> void fn2(int a){} >>>> void fn3(int a,double b){} >>>> int fn4(void){return 0;} >>>> char* fn5(double a){return "";} >>>> >>>> void* table[] = {fn1,fn2,fn3,fn4,fn5}; >>>> >>>> Suggestions to use void(*)(void) for the table element aren't as good. >>> >>> As good as what? >> >> As good as his incorrect and unsafe code? >> >>>> (Because you could actually write table[3]() without the compiler >>>> picking that up. >>> >>> You simply can't do that in C. If that is a deal-breaker, you need a >>> different language. >> >> You can, but you need to wrap things up a bit. > > No you can't. There are various ways to handle the different > function types, but none of them pick up an invalid call as Bart > wants. In every method I can think of you, the programmer, must > state the correct type at (or possible at some time before) the > time of the call. I.e. its up to you to get it right. > >> Something like this would work: >> >> typedef void (*fvoid)(void); >> typedef union { >> void (*v_v)(void); >> void (*v_i)(int); >> void (*v_id)(int, double); >> int (*i_v)(void); >> char* (*pc_d)(double); >> } fvoids; >> >> fvoids table1[] = {{(fvoid) fn1}, {(fvoid) fn2}, {(fvoid) fn3}, >> {(fvoid) fn4}, {(fvoid) fn5}}; >> >> fvoids table2[] = {{.v_v = fn1}, {.v_i = fn2}, {.v_id = fn3}, >> {.i_v = fn4}, {.pc_d = fn5}}; >> >> >> void foo(int x) { >> table1[1].v_i(x); >> } > > But Bart wants to write table[n]() and so on. Does he? I thought he wanted _not_ to be able to write calls in that form (ie, that if written they would be flagged by the compiler). > As for using a union, I am not sure it carries its weight. It > seems simple enough just to cast at the point of call, and not > much more wordy if you define typedefs rather than union members: > > ((v_i_ftype)table[1])(x); An advantage of the union method (with the table2 scheme) is that no casts are required. The appropriate member needs to be accessed in calling the function, but if that is done then everything else is type safe and cast free. > This is well-defined (provided you get the type right) but I am > not 100% sure the union is. Does C guarantee that the > bit-representation of all function pointer types must be the > same? You'll probably get away with it, but I don't think the > compiler /has/ to play ball. ISO C certainly does not guarantee that all function pointer types have the same representation, or even that they have the same size. The table2 initialization method, together with using table2 rather than table1 in the foo() functions (and assuming I haven't missed something), works fine. The table1 scheme is an accident waiting to happen -- ie, undefined behavior, and not the nice kind either like passing a negative int for an unsigned int. In all likelihood (famous last words), it will probably work, but certainly there is no guarantee that it will.