On 4/19/2021 9:42 PM, Chris M. Thomasson wrote:
> On 4/17/2021 10:12 AM, Manfred wrote:
>> (cross-posting to comp.lang.c to let the heat fire up)
>>
>> On 4/17/2021 7:56 AM, Juha Nieminen wrote:
>>> I know that this is probably going to piss off avid C programmers,
>>> but I don't really care all that much.
>>
>> Excellent start
>>
>> [...]
>>>
>>> I myself happen to know both languages quite well, and in fact I
>>> program in both languages as my profession, and I would say that
>>> I am very knowledgeable and proficient in both languages and have
>>> no trouble in developing in either one. I am of the strong opinion
>>> that no, C is not a "simple" language, and that C++ makes so many
>>> things so much simpler, easier and safer.
>>>
>>> I was reminded of this in a quite concrete manner recently when I was
>>> trying to help a beginner programmer who had decided to try his hands
>>> on C, and wanted to create a simple text editor, which would read a
>>> text file, allow the user to edit the text in several ways, and to
>>> save the file.
>>>
>>> It quite quickly turned very frustrating for *both* of us ...
>>
>> The first comment that comes to mind is that probably part of the
>> frustration stems from your approach (no offense) to try and use in C
>> a design that would be suitable for C++ instead. Despite their name,
>> the two are quite different languages. The similarities are about
>> their runtime footprint, not about how they are used.
>>
>> For example, at a first glance the design of string object is not that
>> great for C.
>> A common pattern that pops to the mind is to use opaque structs (see
>> e.g. FILE*), and have something like:
>>
>> typedef struct string_t STRING;
>>
>> STRING* alloc_string(); // creates the empty string
>> STRING* set_string(STRING* s, const char* value);
>> void free_string(STRING* s);
>> STRING* concat_string(STRING* s1, STRING* s2);
>> ...
>>
>> This way there would be no ambiguity about how the STRING object has
>> to be used.
>> And yes, in C you have to do all yourself - C is a low level language,
>> and it is meant for low level stuff. It is hardly the right tool for
>> anything meant to interface with a human user - it is (still) great
>> for system programming and back-end processing, IMO.
>>
>> As second, I agree with Jacob that a text editor is a simple tool, but
>> it is not easy to make expecially for a beginner.
>>
>>
>> [snip]
>
> I would not call C easy. Instead, I would refer to it as being more to
> "the point", perhaps? Things have to be "explicit", it can be more
> verbose. I say that because C does not have RAII. Also, C is really nice
> to create plugins wrt creating portable bindings to other languages.
> Usually in C, things tend go like the following when coding up a new
> "object", so to speak:
>
> Typing in the newsreader, sorry for typos.
> ________________________________
>
> #include <stdio.h>
> #include <stdlib.h>
>
>
> struct foo
> {
> int a;
> void* mem;
> };
>
> int foo_create(struct foo* const self)
> {
> if ((self->mem = malloc(sizeof(42))))
> {
> self->a = 84;
> printf("foo_create(%p)\n", (void*)self);
> return 1;
> }
>
> return 0;
> }
>
> void foo_destroy(struct foo const* const self)
> {
> printf("foo_destroy(%p)\n", (void*)self);
> free(self->mem);
> }
>
> void foo_output(struct foo const* self, FILE* output)
> {
> printf("foo_output(%p)\n", (void*)self);
> fprintf(output, "struct foo(%p)->a = %d\n", (void*)self, self->a);
> fprintf(output, "struct foo(%p)->mem = %p\n", (void*)self, self->mem);
> }
>
>
> int main()
> {
> struct foo foo;
>
> if (foo_create(&foo))
> {
> foo_output(&foo, stdout);
>
> foo_destroy(&foo);
> }
>
> return 0;
> }
> ________________________________
>
>
> See how I have to make an explicit call to foo_destroy? This can be
> handled in C++ in a "cleaner/safer" manner. Again, typing in the
> newsreader sorry for typos. This would be a simple C++ wrapper to the C
> API above:
> ________________________________
>
> struct foo_wrapper
> {
> struct foo m_foo;
>
> foo_wrapper() { if (! foo_create(&m_foo)) throw; }
> ~foo_wrapper() { foo_destroy(&m_foo); }
>
> void output(FILE* output_) const
> {
> foo_output(&m_foo, output_);
> }
> };
>
> ________________________________
>
> Then we can go:
>
> {
> foo_wrapper foo;
> foo.output(stdout);
> }
>
> There. If it fails to create a foo in foo_create, it throws. Otherwise,
> foo_destroy is guaranteed to be called when foo goes out of scope. So,
> C++ can be more convenient, and safer. No need to use all of C++, but it
> does have its moments.
First learning about RAII, even when I learned about ScopeGuard, back in
my C++ days, for some reason, makes me think of the following song:
https://youtu.be/UZ2-FfXZlAU
Even though I was working with C/C++ well before this song was created,
it makes think way back when I was a little kid using basic on my Atari.