Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - kyle

Pages: 1 2 [3] 4
31
Ideas / Re: Built-in recipe handler/make replacement
« on: June 24, 2014, 06:56:42 PM »
Tooling is a big factor in languages.  That is something that I think C2 is doing correctly.  Modules, in particular, are important for that.

Take, for instance, Typescript.  Though I am not a fan of Microsoft (in spite of living within 10km of Microsoft HQ), I think they did a really good job on Typescript.  It has just enough information for tooling (IDE, compiler etc.) to give useful feedback to the programmer.

Java tried to do something about this with the .class files containing enough information to find the dependencies at runtime.  Unfortunately, their scope was too small and they only included code.

Eclipse handles dependencies in the environment via a blizzard of XML files.  Ant and Maven use XML too.  I am not a fan of XML.  "Human" readable? 

I think there is a corollary to Greenspun's Tenth Rule here :-)  Any sufficiently complicated compiler system ends up (badly) implementing part of make :-)

One of the use cases that hits me with some regularity is using Make to do builds for multiple platforms.  I have code that needs to run on Windows and Linux. 

Rather than get bogged down in guessing what a build system needs, how about some larger projects in C2 to find out what would be useful?    Maybe a small web server or something?

I have some small macros that I use heavily in C code, but they evolved over a couple years of use to the point they are now.  I am still working on my resource alloc/dealloc idea though.  If I can get that one, I'll be able to unify a lot of the things I was doing.

Best,
Kyle

32
General Discussion / Re: Interesting article on Modules
« on: June 20, 2014, 11:59:43 PM »
D'oh!  I really should read all the posts before posting.  This is a much better version of Apple's module idea than the slides I posted.  Sigh.

Back to reading.

33
General Discussion / Re: C improvements
« on: June 20, 2014, 11:57:49 PM »
Several compilers did bounds checking on arrays over the years, but the only one I can think of that still has it as an option (you might have to built it with the option) is TCC.

With all the damage that recent security holes in C code have cause where buffer overflows played a big role, maybe it is time to think about bounds checking arrays.

Best,
Kyle

34
Ideas / Re: Weird ideas #3: resource control
« on: June 20, 2014, 11:54:22 PM »
Hmm, now it is gone.  Well...

One of the syntaxes I was playing with was something like this:

Code: [Select]
use_resource(FILE *f=fopen(filename); f; fclose(f)) {
 ... do things to f ...
}

or this:

Code: [Select]
use_resource(int rc = lock_mutex(&m); rc == 0; unlock_mutex(&m)) {
 ... do things with the mutex locked...
}

"use_resource" is not a good name.  The idea is that the construct looks much like a for loop and works a bit like one too.  The first part is the initialization set.  Then the middle part must be true for the block to run.  The end part is what to do when the block terminates.

I have been trying to make this work as a macro hack in C.  I have some ideas, but I am not done.  Something about nested for loops might do it.

I did not know that systemd was using GCC-specific additions like that.  There are a lot of GNU-isms in the Linux kernel, but not usually so much in user space. 

If systemd did not cause enough heat on its own, if it was written in C++, I can't even imagine the flame wars!

Best,
Kyle



35
Ideas / Re: Weird ideas #3: resource control
« on: June 20, 2014, 11:40:24 PM »
Strange, when I read this thread, I don't see my reply, but when I try to reply to it I do.  ???


36
Ideas / Re: Syntax of const pointers
« on: June 20, 2014, 11:34:47 PM »
Hmm, I've been programming in C since about 1979 and I never really thought about all the ways const can be stuffed into a type declaration.  I really like #4.

One of the things that Go changed from C was the order of declarations.  At first this seemed a little bit gratuitous.  However, the more Go code I see, the more it makes sense to me.  It is really a fair amount easier to read complicated declarations.

The Go people also did this to simplify the compiler. 

Thanks for writing all these tables up.  This is really useful!

Best,
Kyle

37
Ideas / Built-in recipe handler/make replacement
« on: June 20, 2014, 11:28:10 PM »
I have read the point about having the compiler handle dependency resolution etc. so that there are fewer tools. 

I would like to point out that Java started that way.  Javac will go find included packages and compile the code if necessary.  However, in practice, this proved to be insufficient and now I almost always see Ant, Maven, or other build systems used instead.  I am not sure of all the reasoning behind this, but I can think of a few things:

0) it never really worked for more than very simple things. 

1) if there is any code generation going on (think yacc), then javac could not handle it.

2) going outside java for some artifacts did not work.  I.e when building a WAR file, you need all the crufty XML, images, static HTML etc. etc. as well as the .jar and/or .class files.

3) NIH syndrome.  Why else would we have make, cmake, scons, etc. etc. etc. etc. etc.

4) changes and additions to the build system required a release of the whole compiler.

I think go does most of the work for compiling Go code.  So, there is some backlash against separate tools.

I am not saying that combining the build system into one tool with the compiler is wrong, but there are some tradeoffs that should be thought about.

One example I really like is git.  The core git code (the plumbing) is a small set of tightly linked programs (they were in shell, now mostly C I think).  On top of that is the "porcelain" that provides varied and higher-level interfaces.   Go is much this way too, I believe. 

The caveat to all this is how does a different executable know how to link together various modules to run a program?  C has several steps to get to an executable.  The linker step is not part of the compiler (conceptually).  In C2 it is.  Modules make this a little trickier.  Certainly the pain of keeping Makefiles up to date is something to be avoided!

How can C2 keep that simplicity but avoid the problems that Java, for instance, ran into?

Best,
Kyle

38
Ideas / Re: Switch statement
« on: June 20, 2014, 11:07:17 PM »
I like having multiple cases combined.  It is a fairly common thing in protocols to see several values that do the same thing (often for support of older versions of a protocol, for instance).

Best,
Kyle

39
Ideas / Re: Weird ideas #2: variable sized structures
« on: June 20, 2014, 11:05:19 PM »
I  talked about two things in my original post.

The first is using the last element in a struct as an zero-element array.  In C this idiom is used to handle variable length arrays of data in binary data:

Code: [Select]
uint8_t buf[4096];

/* read data into buf */

typedef struct {
    int element_count;
    struct foobar[0];
} *foobars;

...

foobars *foos = (foobars)buf;

for(int i=0; i < foos->element_count; i++) {
    .... do something with foos->foobar[i] ...
}

This idiom is very common in protocols.  Sometimes there is a lot more than just a length count at the beginning of the struct.  Here's a really long example from one of my projects:

Code: [Select]
typedef struct {
    /* encap header */
    uint16_t encap_command;    /* ALWAYS 0x006f Unconnected Send*/
    uint16_t encap_length;   /* packet size in bytes - 24 */
    uint32_t encap_session_handle;  /* from session set up */
    uint32_t encap_status;          /* always _sent_ as 0 */
    uint64_t encap_sender_context;  /* whatever we want to set this to, used for
                                     * identifying responses when more than one
                                     * are in flight at once.
                                     */
    uint32_t encap_options;         /* 0, reserved for future use */

    /* Interface Handle etc. */
    uint32_t interface_handle;      /* ALWAYS 0 */
    uint16_t router_timeout;        /* in seconds */

    /* Common Packet Format - CPF Unconnected */
    uint16_t cpf_item_count;        /* ALWAYS 2 */
    uint16_t cpf_nai_item_type;     /* ALWAYS 0 */
    uint16_t cpf_nai_item_length;   /* ALWAYS 0 */
    uint16_t cpf_udi_item_type;     /* ALWAYS 0x00B2 - Unconnected Data Item */
    uint16_t cpf_udi_item_length;   /* REQ: fill in with length of remaining data. */

    /* CM Service Request - Connection Manager */
    uint8_t cm_service_code;        /* ALWAYS 0x54 Forward Open Request */
    uint8_t cm_req_path_size;       /* ALWAYS 2, size in words of path, next field */
    uint8_t cm_req_path[4];         /* ALWAYS 0x20,0x06,0x24,0x01 for CM, instance 1*/

    /* Forward Open Params */
    uint8_t secs_per_tick;        /* seconds per tick */
    uint8_t timeout_ticks;        /* timeout = srd_secs_per_tick * src_timeout_ticks */
    uint32_t orig_to_targ_conn_id;  /* 0, returned by target in reply. */
    uint32_t targ_to_orig_conn_id;  /* what is _our_ ID for this connection, use ab_connection ptr as id ? */
    uint16_t conn_serial_number;    /* our connection serial number ?? */
    uint16_t orig_vendor_id;        /* our unique vendor ID */
    uint32_t orig_serial_number;    /* our unique serial number */
    uint8_t conn_timeout_multiplier;/* timeout = mult * RPI */
    uint8_t reserved[3];            /* reserved, set to 0 */
    uint32_t orig_to_targ_rpi;      /* us to target RPI - Request Packet Interval in microseconds */
    uint16_t orig_to_targ_conn_params; /* some sort of identifier of what kind of PLC we are??? */
    uint32_t targ_to_orig_rpi;      /* target to us RPI, in microseconds */
    uint16_t targ_to_orig_conn_params; /* some sort of identifier of what kind of PLC the target is ??? */
    uint8_t transport_class;        /* ALWAYS 0xA3, server transport, class 3, application trigger */
    uint8_t path_size;              /* size of connection path in 16-bit words
                                     * connection path from MSG instruction.
                                     *
                                     * EG LGX with 1756-ENBT and CPU in slot 0 would be:
                                     * 0x01 - backplane port of 1756-ENBT
                                     * 0x00 - slot 0 for CPU
                                     * 0x20 - class
                                     * 0x02 - MR Message Router
                                     * 0x24 - instance
                                     * 0x01 - instance #1.
                                     */

    uint8_t conn_path[ZLA_SIZE];    /* connection path as above */
} eip_forward_open_request;

The last field, conn_path, uses a macro ZLA_SIZE that is either blank (MSVC) or 0 (GCC).  The second to last field actually contains the length of the connection path.  After I read in a packet, I cast to a pointer to this struct and then I can access all the fields, including the connection path, as if the struct was variable size.

The command I had about variable length arrays was not really related to this.  Sorry for the confusion.

Best,
Kyle



40
Ideas / Re: Weird ideas #1: primitive data type sizes
« on: June 20, 2014, 09:51:32 PM »
The stdint.h intptr_t (I can't remember if it is uintptr_t or intptr_t) is a problem because it must be platform specific.  I do not see a way around it.  A 32-bit platform is a 32-bit platform.  Pointers are 32-bits (unless you use x86 PAE mode in which case they are 48, joy, happiness, sunshine!).  64-bit platforms tend to be more sane.  But there is L32 mode on AMD64 platforms, which gets weird.

This brings up the point that sometimes you actually want to use the platform specific machine word size.  Perhaps this could be "word".  That could be defined as "whatever the CPU likes best."  If you use it, you get a warning about platform dependencies. 

Maybe you have:

int8 - 8-bit integer, signed.
uint8 - 8-bit integer, unsigned.
int16 - 16-bit integer, signed.
...
uint64 - 64-bit integer, unsigned.
int128 - 128-bit integer signed.
uint128 - 128-bit integer unsigned.

float32 - 32-bit IEEE floating point.
float64 - 64-bit IEEE floating point.

iword - CPU integer register size.
fword - CPU floating point register size (also lends itself to jokes in English).
pword - CPU address size.

The last three are very much platform specific and clearly defined as such.

I would not worry about things like real mode, far/near pointers and all the other garbage that x86 systems had over the years (even ARM briefly had a 48-bit addressing mode but no one used it that I know of).  That is rapidly dying.  I would treat it like CPUs that do not have power-of-2 word sizes, ignore it.

41
Ideas / Weird ideas #4: standard bit fields etc.
« on: June 20, 2014, 09:26:22 PM »
Currently, the ordering and some of the semantics of bitfields appear to be implementation dependent.  It would be nice if C2 specified the exact ordering of bit fields and their behavior on all platforms. 

This comes from a discussion I started on LinkedIn in the "Plain Old C Programming" group.  Chris Ryan pointed out the problems with bit fields. 

Best,
Kyle

42
Ideas / Re: Apple's proposed addition of modules to C/C++
« on: June 20, 2014, 09:22:15 PM »
I was not suggesting that C2 adopt the exact system proposed by Apple.  I thought it was an interesting read (as much as you can read slides) to see the issues that they wanted to tackle. 

C2 would not have any need for the backward-compatibility aspects of the Apple proposal.

Eiffel has some interesting ways of importing other modules too.  I will have to look back over the discussion to see if that was already referenced.

It is easy to fall into the trap of doing so much that the common case is painfully verbose (hello, Ada!). 

There are some very interesting things happening in the Javascript world.  Since Javascript has no module/package system of its own, all these methods rely on convention.  Some are quite interesting.  One thing I like is the use of namespaces being passed in to functions that create the module.

Imagine something like this (please excuse the pathetic syntax):

Code: [Select]
namespace libs {
  with mylogging from awesomelogging;
  with yourlogging from waycoollogging;
}


Then I can use libs.mylogging.XYZ.  I have created a package namespace local to my code on the fly.

I am not sure this is useful or makes any sense.  I need to think about it a bit more.  This kind of thing is used in Javascript.  C2 is static, not dynamic here, but I think much of the same thing could be done, if it was useful.

Best,
Kyle

43
Ideas / Re: Preprocessor and Macros
« on: June 19, 2014, 05:13:37 AM »
I've used macros (in anger) to do things like define new syntax:

Code: [Select]
synchronized_block(mutex) {
  ... code that needs to be running in only one thread at once ...
}

This is defined as a macro using two nested for loops and the __LINE__ builtin macro.  It is a hack, but one that eliminated a huge source of bugs during one project.  I am working on some similar stuff for exceptions and resource handling.

The other use I do is to provide default file position arguments for debugging:

Code: [Select]
#define debug_print(m) do {fprintf(stderr,"In %s at line %d: %s\n",__FUNCTION__,__LINE__,m) } while(0)

Assert is often done in macros.

If I am generating tables or something, I tend not to use X-Macros.  I tend to use Perl scripts or something to generate the code. 

I think generics would be really useful.  I've had to build some large macros to do this for things like linked lists.  It's a pain and very easy to do incorrectly.

I agree that things like inlining are best left to the optimizer. 

So, we've got:

* constants -> const
* inlining -> optimizer
* templates -> ?
* syntax extensions -> ?
* assert/debug macros -> ?
* things like default __LINE__ args -> ?

I definitely use the last three more than I probably should :-)

Best,
Kyle


44
Ideas / Weird ideas #3: resource control
« on: June 19, 2014, 03:04:48 AM »
I was not sure what to call this.  One of the things I like about C# is the "using" construct because you can clean up afterward. 

Go has a similar concept, but very different syntactically with deferred functions:

f := open(...)
defer close(f)

I probably have the syntax wrong. 

The idea is that at the end of the block or function, the resource (a file here) is handled.  In this case closed.

I have started trying to build something like this with macros in C but have not gotten very far.

The idea I had was something like this:
Code: [Select]
using(buf = malloc(42)) {
   ... do savage and unnatural things to buf ...
} finally {
   free(buf);
}

The part inside the "using" would allocate or lock or create something and the code inside the finally part would clean it up.  I don't like the syntax because it seems imbalanced (parentheses for allocation and curly braces for clean up), but I like the idea a lot.  If general enough you could use it to lock mutexes, open files etc. etc.

Thoughts?

Best,
Kyle



45
Ideas / Re: Make increment operators statements, not expressions
« on: June 19, 2014, 02:56:30 AM »
A note about this: C's macros make this problem much worse.  It is very easy to not realize that something in a macro is used twice and do this:

 foo = MY_MACRO(b[i++]);

But the macro is defined:

#define MY_MACRO(a) (a + a)

Ooops.

With the macros concept I read here (yay!), this is a lot less risky.  I personally like the idea of making it a statement to simply eliminate the practice, but it is one of the parts of the "C scripture" that might be a harder sell as Bas mentioned.  Maybe a warning is best.

Best,
Kyle

Pages: 1 2 [3] 4