C vs C++

Not about OpenMW? Just about Morrowind in general? Have some random babble? Kindly direct it here.
Chris
Posts: 1368
Joined: 04 Sep 2011, 08:33

Re: C vs C++

Post by Chris » 11 May 2017, 02:05

wareya wrote:I don't really think C++ makes it easy to change the API version of a library on accident. I do think that there's a linker problem, but I think it's exactly the same one that C has, which it does, when glib updates and breaks old userspace programs.
Less "on accident" and more "as an unavoidable consequence of". Take for example:

Code: Select all

class Foo {
    void do_init(int a);

    int var1;

public:
    template<typename T>
    Foo(const T &arg)
    {
        do_init(std::hash(arg));
    }

    int get_var(); /* returns var1 */
    int get_var_info(); /* returns info about var1 */
};
In this case, do_init is clearly an internal method. No user of the Foo class can use that method, or the var1 field, directly. However, if you change do_init to, for example

Code: Select all

    virtual void do_init(int a);
Suddenly preexisting apps are broken, even though from the user's POV the interface is identical. No new function calls, no removed function calls, no different behavior for this class. But because of the nature of templates and virtual class functions, the program implicitly relies on details that doesn't effect how it's used. Similarly, if this change was made:

Code: Select all

    int var1;
    int var2; /* helps track info about var1 */

public:
    int get_var_info(); /* returns info about var1, using var2 for help */
Again, from the POV of the class's user, nothing is different. Everything is still there and does all the same same things for the app, except that internally, it holds a new variable that makes a preexisting function more efficient. But the program implicitly relies on the class's layout to be able to use it, so this change breaks preexisting programs and needs a new version.

If this part of the language was better designed, these breaks wouldn't need to happen. You wouldn't have to come up with workarounds like the pimpl paradigm, or pure virtual classes, both of which do have an effect on run-time performance. Conceptually, fixing this is rather easy. Since (non-virtual) class methods are nothing more than a normal function with an implicit 'this' pointer parameter, do something like this:

Code: Select all

class interface Foo {
    template<typename T>
    static Foo *Make(const T &arg);

    int get_var();
    int get_var_info();
};
In C-like parlance, it would be similar to:

Code: Select all

class Foo; /* Forward-declare Foo class. */
Foo *Foo__Make(const int &arg); /* for each type used with the call */
int Foo__get_var(Foo *this);
int Foo__get_var_info(Foo *this);
This way you can completely separate the interface from the implementation. You can use this class as an opaque interface since the compiler does not need to know anything about the class's internals to construct the necessary calls. The binary interface no longer relies on implementation details. And there's no extra overhead.

If you want to get clever, you can even work in inheritance to selectively expose details to the binary interface where it would be beneficial to do so. For example

Code: Select all

// Base class for intrusive pointer reference counting
class RefBase {
    std::atomic<int> ref = 0;

public:
    virtual ~RefBase() { }

    int add_ref() { return ref.add_and_fetch(1); }
    int dec_ref()
    {
        int ret = ref.sub_and_fetch(1);
        if(!ret) delete this;
        return ret;
    }
};

class interface Foo : public RefBase {
    /* Foo's layout includes and starts with RefBase, but anything more about it (more data fields, more virtuals, etc) is unknown. */

    template<typename T>
    static Foo *Make(const T &arg);

    int get_var();
    int get_var_info();
};
Then with a pointer to Foo you can call add_ref and dec_ref, which can still be inlined since it will know where the atomic variable is in relation to the start of Foo. It's also known that Foo's vtable starts with the destructor since it was inherited, so it can be called as well.

But maybe there's nuances about the language that makes this not possible. Which would be unfortunate since every time I try to write modular C++ code, I wish for something like it.
The accretion he's referring to is to basically consider the API version part of the identity of the library. You wouldn't link "SDL2", you'd link "SDL2 2.0.4".
My interpretation of what he was talking about is a bit more general than that. Rather than an explicit "version", it's more about what each individual function or namespace/class provides and requires. If some lib updates to provide more or require less, you're good, just keep on keeping on. But if it would update to provide less or require more, that's a break. And instead of breaking the function/namespace/whatever, leave it alone and create a new one that sits next to it so it satisfies both old and new programs alike. This way if you link to, for example, SDL-20160425, you can load and run with SDL-20170226 just fine since everything that was there and the things it did is still there doing the same things. You can load and use SDL-20221010 too, though you can't use SDL-19790101. And if you're going to break in a way that old apps can't work with it, make a new library that won't be confused with the old one.
No backwards compatible version changes like semver tries and fails to provide.
The problem with semantic versioning he was pointing out was that it doesn't provide enough context. If the patch number changes, you don't care because you'll still run. If the minor version changes, you don't care you'll still run. If the major version changes, you're screwed. Or really, you might be screwed, which is worse. Even if you successfully link with it, there's little to say what behavior changes there are; it might work, or it might not, or it might work sometimes. So out of the three numbers, you only care about one of them, and that one doesn't tell you all you'd want to know.

So rather than mess about with all that, simply don't break compatibility. "Avoid breakage by turning it into accretion." The entire purpose of that would be to maintain backwards compatibility.
Things like dynamic arrays and variants should by all means be part of the language spec. For some reason they're not.
I'd imagine because things like dynamic arrays and variants can be implemented using the language spec. Unless there's a clear advantage to being in the language spec, leaving them in the standard library reduces the amount of clutter in the language itself. If C is just A+B, then it would be redundant to specify A, B, and C. Just specify A and B, then define C's behavior in terms of A and B.

Obviously nothing like this is so clear cut. I'm sure there are advantages to defining them as core language features. But it becomes of a question if the advantages outweigh the disadvantages.

bidik
Posts: 24
Joined: 28 Apr 2017, 12:06

Re: C vs C++

Post by bidik » 11 May 2017, 15:22

Both are terrible. C feels more lightweight though, compiler wise. C++ has cool data types, better strings, easier to customize. I'll switch to C++ when somebody tells me how to make a 4 KiB exe with it, there is always this weird runtime inside.

User avatar
psi29a
Posts: 3595
Joined: 29 Sep 2011, 10:13
Github profile: https://github.com/psi29a/
Contact:

Re: C vs C++

Post by psi29a » 11 May 2017, 19:48

I can get it down to 6128 Bytes with g++, would love to know how others get it down further. :)

Code: Select all

#include <iostream>

int main()
{
	   std::cout << "Hello World!" << std::endl;
	      return 0;
}

User avatar
lgromanowski
Site Admin
Posts: 1168
Joined: 05 Aug 2011, 22:21
Location: Wroclaw, Poland
Github profile: https://github.com/lgromanowski
Contact:

Re: C vs C++

Post by lgromanowski » 12 May 2017, 06:03

psi29a wrote:I can get it down to 6128 Bytes with g++, would love to know how others get it down further. :)

Code: Select all

#include <iostream>

int main()
{
	   std::cout << "Hello World!" << std::endl;
	      return 0;
}
On Windows you can reduce executable size by not using "default" stuff included in binary: http://www.catch22.net/tuts/reducing-executable-size
best regards,
Lukasz

User avatar
psi29a
Posts: 3595
Joined: 29 Sep 2011, 10:13
Github profile: https://github.com/psi29a/
Contact:

Re: C vs C++

Post by psi29a » 12 May 2017, 06:27

How low can you go Lgro? :D

bidik
Posts: 24
Joined: 28 Apr 2017, 12:06

Re: C vs C++

Post by bidik » 12 May 2017, 07:02

http://www.catch22.net/tuts/reducing-executable-size wrote:The following single C program compiles down to a tiny 480 bytes using Visual C++ 6.0!!
Maybe I should get Visual C++ 6.0 from somewhere. I was really surprised when I heard that some people still use it 20 years after release, but maybe there is something to it. I know this article is from 2001, but maybe I should try.

User avatar
psi29a
Posts: 3595
Joined: 29 Sep 2011, 10:13
Github profile: https://github.com/psi29a/
Contact:

Re: C vs C++

Post by psi29a » 12 May 2017, 08:06

It was asked how to get a C++ program down to 4096 Bytes, no cheating and using C. :)

bidik
Posts: 24
Joined: 28 Apr 2017, 12:06

Re: C vs C++

Post by bidik » 12 May 2017, 08:31

I think it's C++.

return 0 is same in C and C++.

if C and C++ programs do the same thing, they should have the same size. There is just more under the hood in the C++ programs that many people are not aware about, and it's tricky to spot this and take it out. It would require reading headers, I heard that they are hard to understand.

Not knowing what's under the hood is probably good for you though. It doesn't really matter that much, unless you are a perfectionist or something.

User avatar
psi29a
Posts: 3595
Joined: 29 Sep 2011, 10:13
Github profile: https://github.com/psi29a/
Contact:

Re: C vs C++

Post by psi29a » 12 May 2017, 10:30

Getting there: 5468 Bytes :D

Code: Select all

#include <cstdio>
int main()
{
	std::printf("Hello World!\n");
	return 0;
}
bcurtis@WhiteQueen:~/$ g++ test.cpp -m32 -Os -march=native -fomit-frame-pointer -fno-exceptions -fPIC -fPIE -nodefaultlibs -lstdc++ -lc
bcurtis@WhiteQueen:~/$ strip a.out
bcurtis@WhiteQueen:~/$ ./a.out
Hello World!
bcurtis@WhiteQueen:~/$ ls -al ./a.out
-rwxr-xr-x 1 bcurtis bcurtis 5468 May 12 11:31 ./a.out

User avatar
lgromanowski
Site Admin
Posts: 1168
Joined: 05 Aug 2011, 22:21
Location: Wroclaw, Poland
Github profile: https://github.com/lgromanowski
Contact:

Re: C vs C++

Post by lgromanowski » 12 May 2017, 10:35

I thought it would be smaller, but it's not:

Code: Select all

int main() {
  return []()->int{ return 0; }();
}
$ g++ -Os ./main.cpp
$ strip -s ./a.out
$ ls -al ./a.out
-rwxr-xr-x 1 lgromanowski users 6064 05-12 11:34 ./a.out
best regards,
Lukasz

Post Reply

Who is online

Users browsing this forum: No registered users and 3 guests