C vs C++, from the author of ZeroMQ



  • @Salamander said:

    Now I'm not a C/C++ programmer, but won't that do a null dereference if you pass it the start or end of the person list?

    Normally you'd use a circular list either with or without head. Back when I programmed in pure C, clone of Linux list_head was my preferred tool. Of course, now whenever std::vector<something> does not work for me and something like that seems suitable, I just reach for boost::intrusive::list, which is exactly the same thing in a typesafe package.

    @EvanED said:

    1) You can use a circular linked list with a distinguished "end" node and avoid the special cases. (Actually... I'm not quite sure why this isn't more common. Maybe it is and I just don't do enough C to see it, and always see abstractions.)

    It's not a “distinguished end”, it's either head (if the list has a head) or it's the first element again.

    @cvi said:

    catching the exception locally if you need to do some local cleanup

    Actually, when you need to do some local cleanup, you should usually just create a guard that will do it in it's destructor.

    @Nprz said:

    His use of exceptions seem like he missed the point of what exceptions are good for.

    There is a trouble with exceptions. Either you have to be serious about them, or leave them alone, but there is mess in the middle.

    When you decide to use exceptions, you have to write the code to be exception-safe, that is that if exception is thrown through it, it will leave everything in defined state. You'll find yourself using a lot of RAII and std::swapping from temporaries and hiding stuff in pImpls that can be swapped like that. It can be done, but it is a big change in programming style.

    Of course writing exception-safe code has a huge advantage, because such code is also resilient against resource leaks caused by simply forgetting to clean up, so you really should write it anyway. It goes without saying that this has no equivalent in C.

    @dkf said:

    it's quite a lot harder to make a stable ABI with C++ than with C

    Well, yes, I hate that too. Basically in C++ you have to specifically write a façade to hide the actual code behind. And templates (that are otherwise the too you use C++ for) can't be hidden, so when you use them, they are set in stone. Fortunately this problem only matters for system libraries and frameworks.

    @Maciejasjmj said:

    In a sensible language you'd also have a finally block to simplify matters, but in this case you can probably set checking to false in the catch clause just fine.

    That's what the guards are for. Basically you write, once for all similar cases (or get somewhere), something like

    template <typename T> class Restore {
        T old_value;
        T &variable;
    public:
        Restore(T &var, T val) : old_value(var), variable(var) { var = value; }
        ~Restore() { variable = old_value; }
    };
    

    and then you simply

    Restore<bool> rc(check, true);
    

    (it can be improved to infer the type, with a bit of magic even in C++03) instead of check = true. And now it will be restored after exception, but you now also can't forget to restore it.

    @Maciejasjmj said:

    C++ was the best of the worst before, but Bjarne still botched it by insisting on backwards compatibility with C - it exposed way too many naughty bare-metal bits for people to get hurt on.

    Rust is the first language that can do most of the cool things I use in C++ and brings something useful as a bonus. D had the features, but not that much new stuff, but it failed due to disagreement about how standard library should look. Java and C# don't come even close. That does not mean Java or C# would not be better tools for many jobs. Hell, for GUI I'll take Python or JavaScript over C++ any time.

    But most importantly, neither Java nor C# are portable between mobile platforms, so if you want that, you are still stuck with C++ — unless javascript is good enough for you, which, honestly, for many application it now is.



  • @Bulb said:

    @Maciejasjmj said:
    In a sensible language you'd also have a finally block to simplify matters, but in this case you can probably set checking to false in the catch clause just fine.

    That's what the guards are for. Basically you write, once for all similar cases (or get somewhere), something like

    ...

    Or you use something like BOOST_SCOPE_EXIT (which, with C++11 can be made significantly less ugly).



  • My general impression is that it's like he was complaining that modern car is more complex than model T while failing to mention that the modern car is faster and significantly safer.

    For example, he's comparing C code of

    struct foo
    {
        ...
    };
    
    int foo_init ()
    {
        ...
    }
    
    int foo_term ()
    {
        ...
    }
    
    int foo_bar ()
    {
        ...
    }
    

    to C++ code like

    class foo
    {
    public:
        foo () : state (semi_initialised)
        {
             ...
        }
    
        int init ()
        {
            if (state != semi_initialised)
                handle_state_error ();
            ...
            state = intitialised;
        }
    
        int term ()
        {
             if (state != initialised)
                 handle_state_error ();
             ...
             state = semi_terminated;
        }
    
        ~foo ()
        {
             if (state != semi_terminated)
                 handle_state_error ();
             ...
        }
    
        int bar ()
        {
             if (state != initialised)
                 handle_state_error ();
             ...
        }
    };
    

    But that is not equivalent. C++ equivalent of the C code would be just

    class foo
    {
    public:
        int init ()
        {
            ...
        }
    
        int term ()
        {
             ...
        }
    
        int bar ()
        {
             ...
        }
    };
    

    It would die hard if the user failed to call init and term, but so does the C version. The extra code in the C++ version is a cost, but it provides some benefit too, namely additional safety.

    Besides, I can't think of a case where extra variable would be needed. Initialization can only fail if the object holds some resources and a NULL handle can be used to indicate the object was not (successfully) initialized.

    Even more so, deinitialization generally shouldn't fail. What do you do if releasing a resource fails? It can only happen if you cardinally screwed up and are passing invalid handle, in which case trying to salvage it is usually futile, because your state is likely fubar anyway.

    Exception is operations that flush and close a resource like fclose(3). In this case, the destructor should probably assert(3) in debug to make the mistake of forgetting to call the finalizer easier to find (which, again, C can't do at all) and just log and discard it in release (to make the code resilient). It's still better than leaking the handle.


  • Discourse touched me in a no-no place

    @Bulb said:

    Fortunately this problem only matters for system libraries and frameworks.

    What do you think some of us are supporting?



  • […] the very ideology of the object-orientedness makes developers think in specific ways and design their algorithms and data structures accordingly.

    I think this might be his primary and main fault. C++ is not about object-orientation. It is about implicit resource management (RAII) and generic programming and it also happens to support object oriented stuff.

    std::list <person*> people;

    The most stupid think in this is the *. If he put the object in the list by-value, as is usual (unless you need dynamic polymorphism, which in C is pain anyway), the result would be equivalent to the C version and simpler and safer.



  • @dkf said:

    What do you think some of us are supporting?

    Of course I understood you are supporting a library. And yes, I hate the fact maintaining stable library ABI is pain. And even more hate the way Microsoft runtime versions are mutually incompatible (gcc had some ABI transitions too, but it's been quite long since the last one). But it's still relatively small part of all work done in C++.

    In fact the product I work on also comes with a library, but we solved the problem by providing a thin library with C API that controls the actual application over IPC. Fortunately the API does not need to be that large.


  • 🚽 Regular

    @EvanED said:

    C gives you qsort; C++ gives you std::sort. One of those is significantly faster than the other. (Hint: it's not qsort.)

    Why might that be?

    Is something preventing qsort's code from being optimized?
    Something to do with its API or ABI?


  • Discourse touched me in a no-no place

    @Bulb said:

    In fact the product I work on also comes with a library, but we solved the problem by providing a thin library with C API that controls the actual application over IPC. Fortunately the API does not need to be that large.

    I'm supporting a programming language that's pretty widely used industrially. We have our own API→ABI compiler that allows us to keep it extremely stable, much more so than you'd usually get with C (let alone C++, which is far too keen to spray knowledge of things like type sizes around). Some of the techniques we used were inspired by what the Windows API does, and others are based on what ELF dynamic linkers do (but ported to be cross-platform and coupled into our version management infrastructure).

    20 year support of any interface is not simple, especially when you're wanting to be able to evolve that interface as well…


  • Discourse touched me in a no-no place

    @Zecc said:

    Is something preventing qsort's code from being optimized?

    There are other sorts that are potentially faster (qsort depends heavily on having good partitioning) and if they're comparing the sorting of int vs int* there's a whole world of possibilities for complexity right there. (Cache coherency is the most likely candidate for trouble, and that's not something that most performance analyses consider.)


  • 🚽 Regular

    @dkf said:

    There are other sorts that are potentially faster (qsort depends heavily on having good partitioning)

    I wouldn't think qsort necessarily needs to be implemented as a quicksort.

    So if I'm reading you correctly, the problem here is that C doesn't support templates and you can't specialize with different algos for different classes?



  • @Zecc said:

    Is something preventing qsort's code from being optimized?

    Yes. The use of function pointers.

    std::sort is a template. Those are code-generated for each instantiation and the substituted type defines what to call for comparison, so everything is there for the compiler to inline it. On the other hand qsort uses function pointers and is compiled just once and lives in the library. This means it is not available for the compiler for inlining.

    It's a trade-off. The std::sort is almost always inlined, so it's faster, but there is more code. The qsort exists just once, but it is slower due to the overhead of calling otherwise trivial function (comparisons usually are very simple) via indirection.



  • @lightsoff said:

    Because a linked list is almost always the wrong datastructure, especially for a modern target CPU.
    That's nice. They're still pretty common, and I'm not sure why the "circular list with a linear-list interface" isn't a larger proportion. (Or maybe they are. Who knows.)

    @lightsoff said:

    The "next" and "prev" items probably aren't in the CPU cache, and might even be paged out.
    This is totally true. Not just that, but the next/prev pointers themselves decrease cache density. This can make something like vectors faster even in cases where it seems like a list "should" be faster.

    (An example article by Stroustrup.)

    @lightsoff said:

    As you can't even work out how many you have without walking the whole list, they are ludicrously slow.
    Get a less awful list implementation.

    OK, that's a bit 🚎, but plenty of linked list implementations explicitly track the size, and it's not hard to do it yourself with a homegrown list.



  • @Bulb said:

    It's not a “distinguished end”, it's either head (if the list has a head) or it's the first element again.
    "Distinguished head" is probably a better term, but "end" isn't wrong. :-)



  • @Bulb said:

    gcc had some ABI transitions too, but it's been quite long since the last one
    Two nitpicks.

    First, note that there is one going on right now: C++11 mandates some behavior changes that require ABI changes. Now, new features allow that to be applied somewhat on-demand -- a preprocessor macro can control which you get -- but I think it is default with -std=c++11 in the latest GCC version. (I could be wrong.)

    Second, while what you say is true for recent versions supporting the API of older versions, at least IME the reverse is not very true. It's extremely easy to write some source file that will compile with an older (4.x) GCC and run on a system with a recent one, but not vice versa.


  • Banned

    @Bulb said:

    On the other hand qsort uses function pointers and is compiled just once and lives in the library. This means it is not available for the compiler for inlining.

    Wrong. GCC is able to inline some function pointer calls, eliminating indirection. And I believe Clang and MSVC can do it too.



  • @Gaska said:

    Wrong. GCC is able to inline some function pointer calls, eliminating indirection. And I believe Clang and MSVC can do it too.
    Your statement is not wrong, but your conclusion is. GCC and others can inline calls through function pointers if they can determine what the target is, or speculatively optimize (including inlining) based on profiling information.

    But inlining the call to the comparison function requires that there be something to inline it into, and qsort is not available to the compiler or linker to do this, even with link-time optimization.

    If you don't believe me, then try it. Here it is with Ubuntu 14.04 and GCC 4.9 (not this isn't the default version of 14.04):

    $ cat qsort.c
    #include <stdio.h>
    
    int compare(int* a, int * b)
    {
        return *b - *a;
    }
    
    int main()
    {
        static int elements[100] = {0};
        qsort(elements, 100, sizeof(int), compare);
        return elements[0];
    }
    $ gcc -flto -O3 qsort.c
    $ objdump --disassemble a.out | grep '<main>' -A10
    0000000000400440 <main>:
      400440:       48 83 ec 08             sub    $0x8,%rsp
      400444:       b9 70 05 40 00          mov    $0x400570,%ecx
      400449:       ba 04 00 00 00          mov    $0x4,%edx
      40044e:       be 64 00 00 00          mov    $0x64,%esi
      400453:       bf 80 10 60 00          mov    $0x601080,%edi
      400458:       31 c0                   xor    %eax,%eax
      40045a:       e8 b1 ff ff ff          callq  400410 <qsort@plt>
      40045f:       8b 05 1b 0c 20 00       mov    0x200c1b(%rip),%eax
      400465:       48 83 c4 08             add    $0x8,%rsp
      400469:       c3                      retq
    

    Note the call to qsort@plt at 0x40045A. The @plt means it's doing a dynamic call, in this instance in to glibc. (Guess what's not inlined in glibc's version?)

    If you're wondering if -static will change this, it doesn't:

    $ gcc -g -static -flto -O3 qsort.c
    $ gdb a.out --quiet
    Reading symbols from a.out...done.
    (gdb) b compare
    Breakpoint 1 at 0x4010a0: file qsort.c, line 5.
    (gdb) r
    Starting program: /<censored>/a.out
    
    Breakpoint 1, compare (a=0x6c0cc4 <elements+4>, b=0x6c0cc8 <elements+8>) at qsort.c:5
    5           return *b - *a;
    (gdb) p $rip
    $1 = (void (*)()) 0x4010a0 <compare>
    (gdb) up
    #1  0x0000000000406fb1 in msort_with_tmp.part ()
    (gdb) quit
    A debugging session is active.
    
            Inferior 1 [process 31349] will be killed.
    
    Quit anyway? (y or n) y
    $ objdump --disassemble a.out | grep '4010a0:' -B2 -A10
    
    00000000004010a0 <compare>:
      4010a0:       8b 06                   mov    (%rsi),%eax
      4010a2:       2b 07                   sub    (%rdi),%eax
      4010a4:       c3                      retq
      4010a5:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
      4010ac:       00 00 00
      4010af:       90                      nop
    
    00000000004010b0 <__libc_start_main>:
      4010b0:       41 56                   push   %r14
      4010b2:       b8 00 00 00 00          mov    $0x0,%eax
      4010b7:       4d 89 c6                mov    %r8,%r14
    $ objdump --disassemble a.out | grep '406fb1' -B2 -A2
      406fab:       4c 89 e7                mov    %r12,%rdi
      406fae:       41 ff d5                callq  *%r13
      406fb1:       85 c0                   test   %eax,%eax
      406fb3:       7f cb                   jg     406f80 <msort_with_tmp.part.0+0x260>
      406fb5:       41 8b 04 24             mov    (%r12),%eax
    

    See the callq *%r13 at 0x406FAE? That's the indirect call to compare inside qsort.



  • I forgot that I had more to say.

    My previous post illustrates that GCC doesn't do the optimization of which we speak. What about could it? Technically speaking, it could. If the source (IR, really) of qsort were available, you could imagine the compiler performing this optimization.

    However, there is a really big difference between what needs to be done to optimize std::sort vs. qsort. Without inlining, there is just one copy of qsort, but there are potentially multiple instantiations of std::sort. In the latter case, assuming idiomatic C++, the instantiation being called completely determines the sort -- the concrete comparator parameter that is passed to it is completely ignored.

    In the std::sort case, all you need to do to speed things up is inline the comparator's operator() function. Since inlining is something compilers do already (and contributes a large proportion of the benefit of optimization), this presents no problem.

    But qsort is different. If you have multiple qsort calls, the compiler can't inline the comparator call if it keeps just the one version of qsort. So the compiler needs to duplicate it.

    There are two ways this could come about. The first is if the duplication happened as a result of qsort itself being inlined into its callers. But qsort is a very complicated function, and I can't imagine it would be a candidate for inlining. So that option is out.

    The second option is the compiler could see it is called with different parameters in different contexts, and create multiple copies, and then optimize each for its calling context. The term for (the second part of) this is "partial evaluation". But I don't know of any compiler that implements a partial evaluator as an optimization, except so far as you would consider some optimizations like constant propagation being a special case of constant propagation.

    The last hope arises only if your program has a single call to qsort. Maybe the compiler could detect this case and optimize it under that knowledge. This is actually a pretty interesting idea -- might be a neat project for someone to investigate as part of a research project or something like that -- but again, I've never heard of compilers doing something like this.Actually, it looks like GCC does do this, at least in certain circumstances! (Probably, it has to be be static or maybe with LTO.)

    So in short... not only does GCC seem to not make that optimization currently, but it is extremely unlikely to change any time soon.



  • If it is in a shared library, and there is no source for it, the compiler can't inline it. It can inline things from static libraries (with link-time optimization), but not from shared ones. Not the least because it would change the semantics of the shared library, which it must not do.

    And note, that distributions generally don't support static linking of libc at all for at least three different reasons.


  • Banned

    Oh, right, forgot that library functions cannot be optimized almost at all - even moving sqrt() on effectively constant value outside the loop requires some fancy shmancy compiler flags to work.



  • @Bulb said:

    If it is in a shared library, and there is no source for it, the compiler can't inline it.

    @Gaska said:

    library functions cannot be optimized almost at all

    I was going to say this:

    For the reason that I realized when I was halfway through the first post and then wrote up in the second, I think the glibc thing is actually a red herring -- the compiler wouldn't optimize it even if it had the source.

    And that is mostly right, but I tried it and I was wrong about one aspect: It is true if you make two calls with different comparator functions -- it will not produce specialized & partially-evaluated versions of the functions for each. But if you have just one call, it actually will.



  • @EvanED said:

    the compiler wouldn't optimize it even if it had the source.

    No, it probably wouldn't. But you probably could force it to do so with suitable __attribute__s. When it's in a shared library, it is not technically possible.

    @EvanED said:

    And that is mostly right, but I tried it and I was wrong about one aspect: It is true if you make two calls with different comparator functions -- it will not produce specialized & partially-evaluated versions of the functions for each. But if you have just one call, it actually will.

    If you make two calls with different comparator functions, it won't make specialized versions, because they are the same instance and the body is beyond inlining limit. However when you make two calls with different comparator functors, it will, because there is only once call of each instance. Which is why C++ hackers often prefer functors over function pointers.


  • Discourse touched me in a no-no place

    @Bulb said:

    If it is in a shared library, and there is no source for it, the compiler can't inline it.

    But the dynamic loader could, as it has all the information available to it about what the real code is. It still won't for qsort, but that's because it's a recursive algorithm, and there's absolutely no benefit in trying to get that smart. You really don't save very much.

    More trickily, there are reasons for not doing it that are independent. The C++ approach makes the eventual object code much larger (because of all the template specialization) and that can cause icache problems, which is something of a problem with many larger programs. The overall cache performance isn't something that you can really analyse by looking at just the algorithm; it's very closely tied to the details of how it is implemented in actual code. This sort of thing is why there's a difference between benchmark code and production applications, where the cost of having more levels of indirection (especially in things that might be actually nicely in your L1 cache) are offset by having less need to fetch code from main memory in the first place.

    Caches: making algorithmic analysis harder since 1984


  • Banned

    @dkf said:

    But the dynamic loader could, as it has all the information available to it about what the real code is.

    C doesn't have JIT, as far as I know.



  • That has nothing to do with C. The operating system or the CPU would have to have it for the native bytemachinecode.


  • Discourse touched me in a no-no place

    @Gaska said:

    even moving sqrt() on effectively constant value outside the loop requires some fancy shmancy compiler flags to work.

    The only thing required is metadata that says that sqrt() is a function whose result depends only on its arguments. Otherwise there's nothing really to distinguish it from rand() from the perspective of the compiler, which is very much not a safe function to optimise out of a loop that way.


  • Discourse touched me in a no-no place

    @Gaska said:

    C doesn't have JIT, as far as I know.

    There are people working on it, as an extension of LTO. I believe it's still highly experimental.



  • @dkf said:

    The C++ approach makes the eventual object code much larger

    The C++ approach has more problems. It also means that if when a bug is found in std::sort, all programs have to be recompiled to get the fix while programs using qsort will immediately get the fixed version when libc is recompiled.



  • @dkf said:

    There are people working on it, as an extension of LTO. I believe it's still highly experimental.

    Or we'll just switch to having most binaries in LLVM bytecode. After all, it's the same backend (as clang), just deferring the final assembly to just-in-time-time.


  • Banned

    @Bulb said:

    That has nothing to do with C. The operating system or the CPU would have to have it for the native bytemachinecode.

    And neither they have JIT. AFAIK, of course.


  • Banned

    @dkf said:

    The only thing required is metadata that says that sqrt() is a function whose result depends only on its arguments.

    Except it doesn't, because something something FPU errors. Can't find it now, but I've seen a nice explanation on SO once, complete with appropriate GCC flag -f-treat-math-h-functions-like-the-result-for-same-params-is-always-the-same (named a little different) that made it behave like I would expect.


  • Banned

    Oh, and, all the required metadata for sqrt() is in place - GCC devs are rather competent people.


  • Discourse touched me in a no-no place

    @Bulb said:

    Or we'll just switch to having most binaries in LLVM bytecode. After all, it's the same backend (as clang), just deferring the final assembly to just-in-time-time.

    That would be workable; the runtime code issuer is pretty fast, though the main optimiser itself is a bit slower (it's currently tuned for non-JIT use).


  • Discourse touched me in a no-no place

    @Gaska said:

    Except it doesn't, because something something FPU errors.

    Floating point exceptions. For people who are scared of NaN…



  • @Bulb said:

    When it's in a shared library, it is not technically possible.
    I'm not sure about this, but just because qsort is provided by glibc doesn't mean it only has to be provided by glibc, no? So the compiler could get source to qsort when compiling both the client program and the library, and inline calls when it's called in the program?

    Or would ELF symbol preemption/visibility/etc. rules require that the compiler implement it such that someone could preempt qsort with one from a different DSO, which would disqualify that optimization?

    @Bulb said:

    However when you make two calls with different comparator functors, it will, because there is only once call of each instance.
    Huh, I didn't know qsort took a functor parameter.

    @dkf said:

    The C++ approach makes the eventual object code much larger (because of all the template specialization) and that can cause icache problems
    That's a general problem with templates, but unless you're rapidly switching between a bunch of small sort calls with different types I don't think it applies much in this case.

    @Gaska said:

    C doesn't have JIT, as far as I know.
    I think a C/C++ JIT might be an amazingly awesome project. If I were to start a PhD project knowing what I know now and with a free choice of project, that might be an option. (I know of a bunch of systems that do JIT-like things, but for reasons other than performance.)


  • Discourse touched me in a no-no place

    @EvanED said:

    I don't think it applies much in this case.

    Except that it applies to lots of other things too throughout C++. It's not one thing, it's lots of them, and it all adds up. Systems which limit template polymorphism to reference types can much more easily share code (which obviously has consequences).

    As every small child needs to learn for themselves about playgrounds: what time you gain on the swings, you lose on the roundabouts.



  • @EvanED said:

    […] doesn't mean it only has to be provided by glibc, no?

    No; if the header had full code for inlining, it could be inlined. But the header does not have it.

    @EvanED said:

    Huh, I didn't know qsort took a functor parameter.

    qsort does not. std::sort does, though. They are otherwise quite similar code.


  • Discourse touched me in a no-no place

    @EvanED said:

    I think a C/C++ JIT might be an amazingly awesome project.

    There's already people working on it as part of LLVM (and yes, I think it's part of their PhD in compiler system design). I think they're also looking at decompilation so that they can convert object code back to IR, as that would greatly increase the amount that the LTO and JIT could do.

    I'm leeching off some of that in my own project tinkering with LLVM. :D



  • @EvanED said:

    I'm not sure about this, but just because qsort is provided by glibc doesn't mean it only has to be provided by glibc, no? So the compiler could get source to qsort when compiling both the client program and the library, and inline calls when it's called in the program?

    Or would ELF symbol preemption/visibility/etc. rules require that the compiler implement it such that someone could preempt qsort with one from a different DSO, which would disqualify that optimization?

    GCC (and I'm pretty sure others) already perform similar optimizations for well-known functions. E.g., for memcpy():

    void copy( int* aDest, int const* aSrc )
    {
    	memcpy( aDest, aSrc, 32 );
    }
    

    becomes

    copy:
    	movq	(%rsi), %rax
    	movq	%rax, (%rdi)
    	movq	8(%rsi), %rax
    	movq	%rax, 8(%rdi)
    	movq	16(%rsi), %rax
    	movq	%rax, 16(%rdi)
    	movq	24(%rsi), %rax
    	movq	%rax, 24(%rdi)
    	ret
    

    So, no call to memcpy() there. There's a number of standard functions for which this occurs (but maybe qsort() isn't one of them).


  • Discourse touched me in a no-no place

    @cvi said:

    but maybe qsort() isn't one of them

    It definitely won't be. The core of it is a recursive function because it's doing recursive decomposition of the array to be sorted. It would be doing that even if it wasn't actually using the quicksort algorithm; recursive decomposition is required to build a fast general sort. You don't inline a recursive function; it literally makes no sense to do so at all (unless you can compile-time bound the recursion depth, but that would be silly for sorting).

    There might be a wrapper that would be inlineable, but there's very little gain from that. Function calls aren't really all that expensive. (It's not like they're memory allocation or I/O…)


  • Banned

    @EvanED said:

    I'm not sure about this, but just because qsort is provided by glibc doesn't mean it only has to be provided by glibc, no?

    Inlining dynamic libraries kinda violates API contract. And LGPL license.


  • kills Dumbledore

    @Jaloopa's first law: Any forum thread where C++ is mentioned will quickly turn into an in depth discussion about undefined behaviour, exactly what a particular version of a particular compiler does internally with some specific code, and other implementation details that are unreadable to anybody who uses a sensible programming language.

    Pithy, innit


  • Java Dev

    Specifically, this may happen for functions of which GCC has a builtin version. It's probably in the documentation.



  • @dkf said:

    It definitely won't be. The core of it is a recursive function because it's doing recursive decomposition of the array to be sorted. <snip />

    No, but a compiler could -in theory- emit a custom instance of qsort() where the comparator is inlined, and call that instead of the ordinary qsort() (assuming that the comparator is known at the location where qsort() is invoked).



  • @Jaloopa said:

    @Jaloopa's first law: Any forum thread where C++ is mentioned will quickly turn into an in depth discussion about undefined behaviour, exactly what a particular version of a particular compiler does internally with some specific code, and other implementation details that are unreadable to anybody who uses a sensible programming language.

    C++ threads automatically turning into in-depth technical discussions? To me that sounds like quite a nice feature. ;-)



  • It's been way too long since someone posted dumb animal noises.



  • @cvi said:

    @Jaloopa said:
    @Jaloopa's first law: Any forum thread where C++ is mentioned will quickly turn into an in depth discussion about undefined behaviour, exactly what a particular version of a particular compiler does internally with some specific code, and other implementation details that are unreadable to anybody who uses a sensible programming language.

    C++ threads automatically turning into in-depth technical discussions? To me that sounds like quite a nice feature. ;-)

    I takethrow exception to this.

    Which means all the posts in the thread have to be deleted.



  • @powerlord said:

    I takethrow exception to this.

    Which means all the posts in the thread have to be deleted...

    ... and the unhandled exception terminates the server process with extreme prejudice? Isn't that like a core discourse feature?



  • That would explain all the 503 errors.


  • Banned

    @Jaloopa said:

    @Jaloopa's first law: Any forum thread where C++ is mentioned will quickly turn into an in depth discussion about undefined behaviour, exactly what a particular version of a particular compiler does internally with some specific code, and other implementation details that are unreadable to anybody who uses a sensible programming language.

    Pithy, innit


    Everything wrong with @Jaloopa's First Law
    in 2 minutes or less
    (spoilers)
    (duh)
    _

    1. This law doesn't apply in the thread when it was first announced, because the original post is specifically about C++ caveats. *ding*
    2. He is the first one to ever mention UB in this thread. *ding*
    3. He's Java programmer. How do I know? Because the talk about compiler optimizations is incomprehensible to him, and Java programmers are most notorious of not caring about performance ABSOLUTELY AT ALL. *ding*
    4. He's Java programmer. That deserves two sins. *ding* *ding* Three, even.
    5. I don't see anyone here mentioning particular compiler versions. *ding*
    6. He tries to be funny by imitating @blakeyrat but fails miserably. *ding*
    7. He caused me to summon @blakeyrat here, who will now blakerant about how incomprehensible this all shit is to everyone except "C++ 1337h4xx0r dickheads", as he would probably say it. *ding*
      (bonus sin if @blakeyrat rants at me for writing up those two points above since he's already got dragged in this topic, instead of what I said here)
    8. The law implies that talking about compilers on programming forum is spamming *ding* or otherwise inappropriate *ding*.
    9. I was probably wrong about the Java thing - in that case, he's terrible programmer in whatever language he uses because he doesn't care about performance. AT. ALL. *ding*
    10. He's still Java programmer in heart. *ding*
    11. The word "innit" doesn't exist. *ding*
    12. None of the definitions on Urban Dictionary for "innit" mentions anything sexual. Congratulations, you made me lose a bet with a friend. I'll count each definition as a sin. *ding* *ding* *ding* *ding* *ding* *ding* *ding* *ding* *ding* *ding* *ding* *ding* *ding* *ding* *ding* *ding* *ding* *ding* *ding* *ding* *ding* *ding* *ding* *ding* *ding* *ding* *ding*
    13. The wording of the law makes you think C++ is the only language that instantly summons overly technical discussions. Hint: it's not. *ding*
    14. His only post in this whole topic is a hate post. *ding*
    15. No period after the last sentence in his post. *ding*

    TOTAL SIN TALLY: 44
    SENTENCE:
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    C++ CODE REVIEWS

    (in a project using boost::spirit)


  • kills Dumbledore

    @Gaska said:

    1. This law doesn't apply in the thread when it was first announced, because the original post is specifically about C++ caveats. ding

    It was mentioned, wasn't it?

    @Gaska said:

    2. He is the first one to ever mention UB in this thread. ding

    It's an example of the kind of thing that happens in C++ threads

    @Gaska said:

    3. He's Java programmer. How do I know? Because the talk about compiler optimizations is incomprehensible to him, and Java programmers are most notorious of not caring about performance ABSOLUTELY AT ALL. ding

    Nope

    @Gaska said:

    4. He's Java programmer. That deserves two sins. ding ding Three, even.

    Nope

    @Gaska said:

    6. I don't see anyone here mentioning particular compiler versions. ding

    Again, an example of what happens in these threads

    @Gaska said:

    7. He tries to be funny by imitating blakeyrat but fails miserably. ding

    If you didn't find it funny, I guess I'll give you that one

    @Gaska said:

    8. He caused me to summon blakeyrat here, who will now blakerant about how incomprehensible this all shit is to everyone except "C++ 1337h4xx0r dickheads", as he would probably say it. ding

    Don't blame me dude, you summoned him

    @Gaska said:

    9. The law implies that talking about compilers on programming forum is spamming ding or otherwise inappropriate ding.

    Not the intention, just an observation

    @Gaska said:

    11. I was probably wrong about the Java thing - in that case, he's terrible programmer in whatever language he uses because he doesn't care about performance. AT. ALL. ding

    I care about performance. I don't get worked up about exactly what happens under the hood unless I need to to know for the performance improvement I'm chasing

    @Gaska said:

    12. He's still Java programmer in heart. ding

    C++ was the first language I used. Damn, you really hate Java don't you?

    @Gaska said:

    13. The word "innit" doesn't exist. ding

    UK slang. Short for "isn't it"

    @Gaska said:

    14. None of the definitions on Urban Dictionary for "innit" mentions anything sexual. Congratulations, you made me lose a bet with a friend. I'll count each definition as a sin. ding ding ding ding ding ding ding ding ding ding ding ding ding ding ding ding ding ding ding ding ding ding ding ding ding ding ding

    Don't make stupid bets then

    @Gaska said:

    42. The wording of the law makes you think C++ is the only language that instantly summons overly technical discussions. Hint: it's not. ding

    Maybe. I tend to notice it mostly in C++, but that's probably confirmation bias

    @Gaska said:

    43. His only post in this whole topic is a hate post. ding

    I'm a lover, not a hater

    @Gaska said:

    44. No period after the last sentence in his post. ding

    To be a proper EWW, this should have called me racist

    Thanks for playing


Log in to reply