Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #112714 > unrolled thread
| Started by | Thiago Adams <thiago.adams@gmail.com> |
|---|---|
| First post | 2017-06-20 11:02 -0700 |
| Last post | 2017-07-08 11:15 -0700 |
| Articles | 11 — 4 participants |
Back to article view | Back to comp.lang.c
Patterns for C code with code generation (new ,delete ,destroy , { }, dismiss ) Thiago Adams <thiago.adams@gmail.com> - 2017-06-20 11:02 -0700
Re: Patterns for C code with code generation (new ,delete ,destroy , { }, dismiss ) Thiago Adams <thiago.adams@gmail.com> - 2017-06-20 11:13 -0700
Re: Patterns for C code with code generation (new ,delete ,destroy , { }, dismiss ) Ben Bacarisse <ben.usenet@bsb.me.uk> - 2017-06-20 20:09 +0100
Re: Patterns for C code with code generation (new ,delete ,destroy , { }, dismiss ) Thiago Adams <thiago.adams@gmail.com> - 2017-06-20 13:08 -0700
Re: Patterns for C code with code generation (new ,delete ,destroy , { }, dismiss ) Ben Bacarisse <ben.usenet@bsb.me.uk> - 2017-06-20 22:56 +0100
Re: Patterns for C code with code generation (new ,delete ,destroy , { }, dismiss ) Thiago Adams <thiago.adams@gmail.com> - 2017-06-21 04:44 -0700
Re: Patterns for C code with code generation (new ,delete ,destroy , { }, dismiss ) scott@slp53.sl.home (Scott Lurndal) - 2017-06-20 20:20 +0000
Re: Patterns for C code with code generation (new ,delete ,destroy , { }, dismiss ) Thiago Adams <thiago.adams@gmail.com> - 2017-06-20 14:02 -0700
Re: Patterns for C code with code generation (new ,delete ,destroy , { }, dismiss ) GOTHIER Nathan <nathan.gothier@gmail.com> - 2017-06-21 05:33 +0200
Re: Patterns for C code with code generation (new ,delete ,destroy , { }, dismiss ) Thiago Adams <thiago.adams@gmail.com> - 2017-06-21 04:29 -0700
Re: Patterns for C code with code generation (new ,delete ,destroy , { }, dismiss ) Thiago Adams <thiago.adams@gmail.com> - 2017-07-08 11:15 -0700
| From | Thiago Adams <thiago.adams@gmail.com> |
|---|---|
| Date | 2017-06-20 11:02 -0700 |
| Subject | Patterns for C code with code generation (new ,delete ,destroy , { }, dismiss ) |
| Message-ID | <e2fa453b-5161-4cd1-a96a-2246eba1e4fc@googlegroups.com> |
I am trying to identify some patterns in C code.
I manually apply these patterns on my own code that is a C parser.
I imagine how the code could be simplified if I had some language extensions and generators for these patterns.
I would like to know what do you think about the following patterns and the respective language extensions that could be implemented in a "C with extensions" to C translator.
Patterns:
1) Initialization
------------------
typedef struct
{
int x;
int y;
} Point;
#define PERSON_INIT (Point) {1, 2}
Point pt = POINT_INIT;
pt = POINT_INIT
Replaced by:
typedef struct
{
int x = 1;
int y = 2;
} Point;
Point pt = {};
pt = {}
The "constructor of pt" is not called automatically like C++
but the compiler must track initialization like any other types
int etc.
2) Destroy
----------
typedef char* String;
void String_Destroy(String* p)
{
free(*p);
}
typedef struct
{
String name;
} Person;
#define PERSON_INIT {NULL}
void Person_Destroy(Person* p)
{
String_Destroy(&p->name);
}
Replaced by:
typedef char* String;
void operator destroy(String* p)
{
free(*p);
}
typedef struct
{
String name;
} Person;
//void operator destroy(Person* p) is generated
//automatically calling destroy of name.
/*
void operator destroy(Person* p)
{
//custom destroy for Person
destroy p->name;
}
*/
int main()
{
Person person = {};
destroy person; //ok
}//ok
or just
int main()
{
Person person = {};
}//ok
In this case the compiler must be able in compile time
to determine that destroy was not called. Also determine that
the variable was initialized.
int main()
{
Person person;
} //error: person not initialized
int f(int i)
{
Person person = {};
if (i > 1)
{
destroy person;
}
} //error: destroy may be called before
int f(int i)
{
Person person = {};
if (i > 1)
{
destroy person;
}
else
{
destroy person;
}
} //ok
In very complicated scenarios we can dismiss the compiler error.
int f(int i)
{
Person person = {};
....
// complicated code will destroy or move person
....
dismiss person; //ok
}
3) New
Person* Person_Create()
{
Person* p = malloc(sizeof * p);
if (p)
{
*p = PERSON_INIT;
}
return p;
}
Replaced by:
auto generated operator new.
or custom operator new
String* operator new();
4) Delete
void Person_Delete(Person* p)
{
if (p)
{
Person_Destroy(p);
free(p);
}
}
Replaced by:
auto generated operator delete.
or custom operator delete
void operator delete(Person*);
5) Destroy for pointers
typedef struct
{
Y *pY;
} X;
#define X_INIT {NULL}
void X_Destroy(X* p)
{
Y_Delete(p->pY);
}
Replaced by:
typedef struct
{
Y ^pY;
} X;
'^' works like a pointer:
But, when generating destroy tells the generator that
Y_Delete(p->pY) must be called.
int main()
{
Person ^ person;
delete person; //error person not initialized
}
int main()
{
Person ^ person = {};
} //ok delete person called (if null no effect)
int main()
{
Person ^ person = new Person;
delete person; //ok
}
int main()
{
Person ^ person = new Person;
delete person; //ok
delete person; //error: delete may be called before
}
int main()
{
Person * person = new Person;
delete person; //error this is not a ^ pointer
}
int main()
{
Person ^ person = new Person;
destroy person;
*person = (Person){}; //ok
}//ok
int main()
{
Person ^ person = {};
delete person; //ok
*person = new Person;
}//ok
[toc] | [next] | [standalone]
| From | Thiago Adams <thiago.adams@gmail.com> |
|---|---|
| Date | 2017-06-20 11:13 -0700 |
| Message-ID | <f43b07dc-5164-45a8-87c5-074f098adabb@googlegroups.com> |
| In reply to | #112714 |
On Tuesday, June 20, 2017 at 3:02:51 PM UTC-3, Thiago Adams wrote:
[...]
> '^' works like a pointer:
> But, when generating destroy tells the generator that
> Y_Delete(p->pY) must be called.
>
[...]
>
> int main()
> {
> Person ^ person = {};
> } //ok delete person called (if null no effect)
To create something like C++ smart pointers.
typedef struct
{
int counter = 1;
} RefCounted;
void operator delete (RefCounted* p)
{
if (p)
{
p->counter--;
if (p->counter == 0)
{
destroy p;
}
}
}
int main()
{
RefCounted^ p = new RefCounted;
RefCounted^ p2 = NULL;
p2 = addref(p); //increments p->count
}//delete p and delete p2 are called
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2017-06-20 20:09 +0100 |
| Message-ID | <87zid2a32f.fsf@bsb.me.uk> |
| In reply to | #112716 |
Thiago Adams <thiago.adams@gmail.com> writes:
> On Tuesday, June 20, 2017 at 3:02:51 PM UTC-3, Thiago Adams wrote:
>
> [...]
>
>> '^' works like a pointer:
>> But, when generating destroy tells the generator that
>> Y_Delete(p->pY) must be called.
>>
>
> [...]
>
>>
>> int main()
>> {
>> Person ^ person = {};
>> } //ok delete person called (if null no effect)
>
>
>
> To create something like C++ smart pointers.
>
> typedef struct
> {
> int counter = 1;
> } RefCounted;
>
>
> void operator delete (RefCounted* p)
> {
> if (p)
> {
> p->counter--;
> if (p->counter == 0)
> {
> destroy p;
> }
> }
> }
I don't see how this is like C++'s special pointer templates. They are
parameterised types and these have no type parameter.
> int main()
> {
> RefCounted^ p = new RefCounted;
> RefCounted^ p2 = NULL;
>
> p2 = addref(p); //increments p->count
Shouldn't that be automatic since you've gone to the trouble of having a
new type syntax? A missing addref and it all goes wrong.
Also, assignment should call delete on the pointer assigned to, should
it not? After all it's losing reference.
> }//delete p and delete p2 are called
--
Ben.
[toc] | [prev] | [next] | [standalone]
| From | Thiago Adams <thiago.adams@gmail.com> |
|---|---|
| Date | 2017-06-20 13:08 -0700 |
| Message-ID | <ee7dd859-978b-4724-8bba-c4aa267815c2@googlegroups.com> |
| In reply to | #112725 |
On Tuesday, June 20, 2017 at 4:09:52 PM UTC-3, Ben Bacarisse wrote:
> Thiago Adams writes:
>
> > On Tuesday, June 20, 2017 at 3:02:51 PM UTC-3, Thiago Adams wrote:
> >
> > [...]
> >
> >> '^' works like a pointer:
> >> But, when generating destroy tells the generator that
> >> Y_Delete(p->pY) must be called.
> >>
> >
> > [...]
> >
> >>
> >> int main()
> >> {
> >> Person ^ person = {};
> >> } //ok delete person called (if null no effect)
> >
> >
> >
> > To create something like C++ smart pointers.
> >
> > typedef struct
> > {
> > int counter = 1;
> > } RefCounted;
> >
> >
> > void operator delete (RefCounted* p)
> > {
> > if (p)
> > {
> > p->counter--;
> > if (p->counter == 0)
> > {
> > destroy p;
> > }
> > }
> > }
>
> I don't see how this is like C++'s special pointer templates. They are
> parameterised types and these have no type parameter.
Sample:
In C++:
int main()
{
std::unique_ptr<Person> person = new Person;
}
In "C extended"
int main()
{
Person^ person = new Person;
}
No need for template type.
> > int main()
> > {
> > RefCounted^ p = new RefCounted;
> > RefCounted^ p2 = NULL;
> >
> > p2 = addref(p); //increments p->count
>
> Shouldn't that be automatic since you've gone to the trouble of having a
> new type syntax? A missing addref and it all goes wrong.
>
> Also, assignment should call delete on the pointer assigned to, should
> it not? After all it's losing reference.
>
> > }//delete p and delete p2 are called
>
I would like to keep the rules very simple.
Person^ pPerson = new Person;
pPerson = new Person; //error: delete not called!
or no error at all at first version.
So Person^ pPerson is not too magic.
It's just a pointer, but someone asked to call delete
on it or to remember you to call delete on it.
When used on struct members, it's used to generate the destroy function.
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2017-06-20 22:56 +0100 |
| Message-ID | <87tw3a9vc6.fsf@bsb.me.uk> |
| In reply to | #112734 |
Thiago Adams <thiago.adams@gmail.com> writes:
> On Tuesday, June 20, 2017 at 4:09:52 PM UTC-3, Ben Bacarisse wrote:
>> Thiago Adams writes:
>>
>> > On Tuesday, June 20, 2017 at 3:02:51 PM UTC-3, Thiago Adams wrote:
>> >
>> > [...]
>> >
>> >> '^' works like a pointer:
>> >> But, when generating destroy tells the generator that
>> >> Y_Delete(p->pY) must be called.
>> >>
>> >
>> > [...]
>> >
>> >>
>> >> int main()
>> >> {
>> >> Person ^ person = {};
>> >> } //ok delete person called (if null no effect)
>> >
>> >
>> >
>> > To create something like C++ smart pointers.
>> >
>> > typedef struct
>> > {
>> > int counter = 1;
>> > } RefCounted;
>> >
>> >
>> > void operator delete (RefCounted* p)
>> > {
>> > if (p)
>> > {
>> > p->counter--;
>> > if (p->counter == 0)
>> > {
>> > destroy p;
>> > }
>> > }
>> > }
>>
>> I don't see how this is like C++'s special pointer templates. They are
>> parameterised types and these have no type parameter.
>
> Sample:
<snip>
> In "C extended"
>
> int main()
> {
> Person^ person = new Person;
> }
>
> No need for template type.
I don't see the connection with what I commented on, but I'm not sure
there is much pint in my trying to understand. In future I'll I assume
that it will all work.
<snip>
--
Ben.
[toc] | [prev] | [next] | [standalone]
| From | Thiago Adams <thiago.adams@gmail.com> |
|---|---|
| Date | 2017-06-21 04:44 -0700 |
| Message-ID | <651ff6b9-85f1-4f53-903c-142b2bf235b5@googlegroups.com> |
| In reply to | #112741 |
On Tuesday, June 20, 2017 at 6:56:47 PM UTC-3, Ben Bacarisse wrote:
> Thiago Adams writes:
>
> > On Tuesday, June 20, 2017 at 4:09:52 PM UTC-3, Ben Bacarisse wrote:
> >> Thiago Adams writes:
> >>
> >> > On Tuesday, June 20, 2017 at 3:02:51 PM UTC-3, Thiago Adams wrote:
> >> >
> >> > [...]
> >> >
> >> >> '^' works like a pointer:
> >> >> But, when generating destroy tells the generator that
> >> >> Y_Delete(p->pY) must be called.
> >> >>
> >> >
> >> > [...]
> >> >
> >> >>
> >> >> int main()
> >> >> {
> >> >> Person ^ person = {};
> >> >> } //ok delete person called (if null no effect)
> >> >
> >> >
> >> >
> >> > To create something like C++ smart pointers.
> >> >
> >> > typedef struct
> >> > {
> >> > int counter = 1;
> >> > } RefCounted;
> >> >
> >> >
> >> > void operator delete (RefCounted* p)
> >> > {
> >> > if (p)
> >> > {
> >> > p->counter--;
> >> > if (p->counter == 0)
> >> > {
> >> > destroy p;
> >> > }
> >> > }
> >> > }
> >>
> >> I don't see how this is like C++'s special pointer templates. They are
> >> parameterised types and these have no type parameter.
> >
> > Sample:
> <snip>
> > In "C extended"
> >
> > int main()
> > {
> > Person^ person = new Person;
> > }
> >
> > No need for template type.
>
> I don't see the connection with what I commented on, but I'm not sure
> there is much pint in my trying to understand. In future I'll I assume
> that it will all work.
For shared objects, we need to provide custom destroy for each type.
To provide one custom destroy for many types, then templates would be necessary.
For the normal pointer, similar of unique_ptr the default(generated) destroy works.
I wrote the smart pointer case here but my intention was not to talk about C++. At some point "C with extensions" can be compared with "C++" to justify its creation. Maybe an C++ user group is more appropriated - or not.
What I would like to known is if these patterns apply for
your current C code, and secondly if the extended code has advantages
over the manual code.
[toc] | [prev] | [next] | [standalone]
| From | scott@slp53.sl.home (Scott Lurndal) |
|---|---|
| Date | 2017-06-20 20:20 +0000 |
| Message-ID | <7sf2B.23032$xk.7904@fx39.iad> |
| In reply to | #112716 |
Thiago Adams <thiago.adams@gmail.com> writes:
>On Tuesday, June 20, 2017 at 3:02:51 PM UTC-3, Thiago Adams wrote:
>
>[...]
>
>> '^' works like a pointer:
>> But, when generating destroy tells the generator that
>> Y_Delete(p->pY) must be called.
>>
>
>[...]
>
>>
>> int main()
>> {
>> Person ^ person = {};
>> } //ok delete person called (if null no effect)
>
>
>
>To create something like C++ smart pointers.
(note that the below won't work in a multithreaded application
without change)
>
>typedef struct
>{
> int counter = 1;
>} RefCounted;
>
>
>void operator delete (RefCounted* p)
>{
> if (p)
> {
> p->counter--;
> if (p->counter == 0)
These two tests must be done atomically
(e.g. __sync_add_and_fetch() in gcc)
> {
> destroy p;
> }
> }
>}
>
>int main()
>{
> RefCounted^ p = new RefCounted;
> RefCounted^ p2 = NULL;
>
> p2 = addref(p); //increments p->count
Addref is undefined, but it must also use an
atomic increment.
>
>}//delete p and delete p2 are called
>
>
[toc] | [prev] | [next] | [standalone]
| From | Thiago Adams <thiago.adams@gmail.com> |
|---|---|
| Date | 2017-06-20 14:02 -0700 |
| Message-ID | <baf211e6-fb65-4037-ba3b-ecdffa392d78@googlegroups.com> |
| In reply to | #112738 |
On Tuesday, June 20, 2017 at 5:20:25 PM UTC-3, Scott Lurndal wrote:
> Thiago Adams writes:
[...]
> Addref is undefined, but it must also use an
> atomic increment.
Yes I known. There are more differences.
The shared_ptr don't use an intrusive counter and it has one more
counter for week_ptr.
The main point is not the smart pointer implementation but the feature
that calls "delete" on the variable.
Gcc has __attribute__((cleanup(…))):
"The cleanup attribute runs a function when the variable goes out of scope."
The reason I don't like this feature is because it doesn't work with struct members.
I think the two problems are related. Stack variables and struct data members. What they have in common is that the memory can be invalidated (when the function returns or when the struct is destroyed)
Sample:
typedef struct
{
Y ^p;
} X;
int main()
{
X x;
x.p = new Y;
X ^pX = new X;
}
[toc] | [prev] | [next] | [standalone]
| From | GOTHIER Nathan <nathan.gothier@gmail.com> |
|---|---|
| Date | 2017-06-21 05:33 +0200 |
| Message-ID | <20170621053327.5433b93794334be9cb09f27f@gmail.com> |
| In reply to | #112714 |
On Tue, 20 Jun 2017 11:02:43 -0700 (PDT)
Thiago Adams <thiago.adams@gmail.com> wrote:
> Replaced by:
>
> typedef struct
> {
> int x = 1;
> int y = 2;
> } Point;
I don't believe this definition means the same thing as the previous one since
the structure members are initialized before defining the new type name. It
looks like a very bad way to define a constant structure.
I would write instead something like:
struct pt
{
int pt_cx;
int pt_cy;
};
const struct pt PT_CONST = {1, 2};
[toc] | [prev] | [next] | [standalone]
| From | Thiago Adams <thiago.adams@gmail.com> |
|---|---|
| Date | 2017-06-21 04:29 -0700 |
| Message-ID | <8bf985f6-ebc9-4813-a87d-9f6ae1b89d86@googlegroups.com> |
| In reply to | #112753 |
On Wednesday, June 21, 2017 at 12:37:02 AM UTC-3, GOTHIER Nathan wrote:
> On Tue, 20 Jun 2017 11:02:43 -0700 (PDT)
> Thiago Adams wrote:
>
> > Replaced by:
> >
> > typedef struct
> > {
> > int x = 1;
> > int y = 2;
> > } Point;
>
> I don't believe this definition means the same thing as the previous one since
> the structure members are initialized before defining the new type name. It
> looks like a very bad way to define a constant structure.
>
When we read the type, we don't read "x is initialized with 1".
We read: "1 will be used in the default initialization for x"
> I would write instead something like:
>
> struct pt
> {
> int pt_cx;
> int pt_cy;
> };
>
> const struct pt PT_CONST = {1, 2};
With this approach we lose the ability to compose.
//error
const struct line LINE_CONST = {PT_CONST , PT_CONST };
[toc] | [prev] | [next] | [standalone]
| From | Thiago Adams <thiago.adams@gmail.com> |
|---|---|
| Date | 2017-07-08 11:15 -0700 |
| Message-ID | <22b85522-ac83-4dd9-82c9-452d3fa14451@googlegroups.com> |
| In reply to | #112714 |
On Tuesday, June 20, 2017 at 3:02:51 PM UTC-3, Thiago Adams wrote: > I am trying to identify some patterns in C code. > I manually apply these patterns on my own code that is a C parser. > > I imagine how the code could be simplified if I had some language extensions and generators for these patterns. > > I would like to know what do you think about the following patterns and the respective language extensions that could be implemented in a "C with extensions" to C translator. I did some progress in my C parser and I managed to generated code with #includes, #defines, #ifdef, spaces and comments. Every token is inside the AST. I can rebuild the original source as desired. (I can remove comments or reformat). Some invisible tokens had to be added to control macros, #if, #include. Now I think it's possible to implement the language extensions I want having an natural output without some problems that a code 100% expanded can have. As soon the main bugs of the compiler are solved, I think it became useful in production code because we can review and thrust the generated code. I also want to implement typeof and conversions of C11 to C89. The first implementation I did is the initialization: The left side is the source and right side is the generated code. https://www.youtube.com/watch?v=lIRLijA_n2Q&feature=youtu.be Would you use something like this? This "full AST" is useful for refactoring as well and to create tools like doxygen.
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.c
csiph-web