Announcement

Collapse
No announcement yet.

John Carmack's Comments On C/C++

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • #21
    Originally posted by F i L View Post
    Not really. The language could be fully strong-typed, but also support typeless parameters. The compiler would analyse the function, and determine the restrictions each typeless parameter required, then give compiler errors if the code tries to pass a variable which doesn't meet those restrictions. These functions would be template function, which new version being compiled out with each unique set of param types used (therefor, special restrictions would be required for key Shared Object functions). eg:

    Code:
    func foo(x, y:int) # 'x' is typeless, 'y' must be an int
    {
        return x * y
    }
    
    func main
    {
        var s = "text" # 's' is text
        var i = 0      # 'i' is an int
        var f = 0.0    # 'f' is a float
    
        var r = foo(s, i) # error: can't pass text as a first parameter
        var r = foo(f, i) # works: because compiler can multiply a 'int' and a 'float'
        var r = foo(i, i) # works: compiler can compile 'int' and 'int'
    }
    In the code above, two versions of 'foo' would be compiled out... one taking in a (float, int), and one taking in an (int, int).

    As far as memory safety without garbage-collection, the language could define a distinction between a 'var' (a memory "owner" which can't be null), and a 'ref' (a memory reference, which can't "own" memory). Vars would always be deleted at the end of their scope (unless returned), or when removed from arrays, etc.. whereas References to that data would simple be set to null. example:

    Code:
    func main
    {
        ref x : int
        ref y : num
        
        scope
        {
            var z = 0
            var w = 0.0
            
            x => z
            x = 1 # sets 'z' to 1
            
            y => w
            y = 1.0 # sets 'w' to 1.0
            
            # auto cleanup code injected here
            # In this case, the code would look like:
            # 
            #    x => null
            #    Memory.delete(z)
            #    Memory.delete(w)
            #
            # Optimization: We don't need to set 'y' to null
            # because it's not directly accessed after this scope.
            # 
            # PS. Technically, we also don't need to delete 'z' & 'w'
            # since they would be created on the stack, but I put it
            # in to illustrate what normally happens with heap vars.
        }
        
        x = 2 # error: 'x' is null
        
        var n = 2.0
        
        y => n
        y = 3.0 # sets 'n' to 3.0
        
        # auto cleanup code:
        # 
        #    Memory.delete(n)
    }
    There's a lot of other stuff to that, but I think it would be possible to do something like that to achieve memory safety without a GC or Ref-Counting. You'd also need a low-level 'ptr' type which wouldn't have any restrictions and require manual memory management for advanced situations, etc..
    It seems you're describing Vala.

    Comment


    • #22
      As I'm old school (and embedded developer) I still use C. But even with C, my coding rules ban me from using some features of the language. The thing is that C++ has many features that leave a program indecipherable, like operator overloading. Yes, it is a powerful tool, yet it is often misused and makes the code hard to understand.
      Being a C programmer, I've always liked Java for it's simplicity. But then again, Java developers tend to abuse OOP paradignms just for the sake of having beautiful object model design. If I had to ask something to Java is unsafe contexts and an easier JNI, just like C# has them.
      Anyway, If you write C code, you get a better glimpse of what machine code the compiler may generate.
      Last edited by newwen; 15 January 2013, 05:49 AM.

      Comment


      • #23
        Originally posted by susikala View Post
        Overall, I believe the discussion about which programming language is better and specifically spending time on discussing to which extent constants or likewise should be used or not is pointless. Most programming languages are the same and the differences are mostly, in my opinion, a question of personal preference and a lot of ego.
        Not necessarily. It does come down to the developer's preferences and background, of course, but talking about it is also about giving information. Not everyone is aware of all the languages and may want to try some others out, or some might have an outdated view about one etc. So information helps.

        Originally posted by susikala View Post
        What I actually think one of the overlooked sides of many languages is intuitivity. If you're already designing a high-level language which is meant to be used by humans, why not do it right?
        Indeed. That's again one point that I like about D, it gets things done fairly intuitively. Its stdio provides writeln(), for instance - no more need to append "\n" all the time, and no more need to write substitution characters in the string, just like in Pascal, but then it also provides the more traditional functions in case you need some extra functionality. And its library is rather extensive and has structures and functions to handle things like time, CPU capabilities, regular expressions etc.

        I still find it a bit odd how templates are used in D (variable.template!(type) or template!(type)(variable)), but I don't suppose there is any better way of representing those.

        Having a background of UnrealScript coding, D just feels the closest thing to it. UnrealScript doesn't use the concept of pointers, for instance, and so does D if you so desire (although it's flexible and will let you use them if you want it). Both are object-oriented, both have very useful utility functions out of the box. Both use the C coding style while having Java-like high-level additions. It's just too bad that D doesn't have certain bindings in active maintenance (like Qt, DBUS, etc.), so its use is somewhat limited in that regard.

        Comment


        • #24
          Originally posted by joe_gunner View Post
          D is a niche language that's far too complex and likely never gonna get finished due to it's complexity and ever increasing number of bolt-on features.
          Indeed. D v1 was good, but they just can’t stop adding stuff to D2, and now it’s even more complicated than C++.
          Which is why I’m now trying Go
          Last edited by stqn; 15 January 2013, 09:08 AM.

          Comment


          • #25
            Originally posted by GreatEmerald View Post
            Yeap, that's what I mean. I don't see how using operators there is helping, all it does is make the code look inconsistent. Having a function that does all the stream piping for you simply makes more sense. Sure, you can use the operators for doing other things as well, but most of the time you don't need that. And the fact that it's an operator and not a function is also something I don't particularly like, especially given that it clashes with bitwise shifting operators.
            I think the insertion/extraction stream operators are strangely appropriate ... maybe it's grown on me.
            Here are the advantages:
            • No longer need to remember type specifiers
            • Can easily overload new << and >> operators for new types
            • Can easily cascade them (see below)


            As for cascading:

            Which is most practical?
            Code:
            std::cout << "Today is January ," << 15 << ", " << 2013 << std::endl;
            Or the equivalent
            Code:
            Output(Output(Output(Output(Output(std::cout, "Today is January ,"), 15), ", "), 2013), '\n');
            I can't say I advocate operator overloading for most problems, but the ability to cascade them seems to warrant their use for outputting formatted text.

            Comment


            • #26
              Originally posted by nslay View Post
              Which is most practical?
              How about this:
              Code:
              writeln("Today is January ", 15, ", ", 2013);
              Or even:
              Code:
              printf("Today is January %d, %d\n", 15, 2013);

              Comment


              • #27
                Without making too much of a C-fanboyism point, I always thought that printf was the best, most simple and most elegant solution to combining logic and presentation. It actually does it very well, in strictly separating presentation and logic. Every other solution, including using <<, ., + or simply tons of function calls, or at the ugliest the php way of jumping fro and to html, always feels wrong.

                Comment


                • #28
                  Originally posted by GreatEmerald View Post
                  How about this:
                  Code:
                  writeln("Today is January ", 15, ", ", 2013);
                  Or even:
                  Code:
                  printf("Today is January %d, %d\n", 15, 2013);
                  The first one would never work in C. See stdarg.h for more information. In C++11, you can use variadic templates for a writeln() function as you described. But then, at the very worst, the compiler will produce a unique function for every output format you use. More likely, the compiler will inline such templated writeln() functions.

                  The second example requires you remember %d for outputting integers. That's OK for basic types. But what about system typedefs like size_t? Is that %u, %lu, or %llu? This is such a problem that C99 introduced macros in stdint.h for printf and scanf type specifiers for integer typedefs.

                  In all fairness, the compiler usually corrects misuses of printf, or at the very least prints a warning.

                  Neither example allows you to extend input/output to new types (in fact, it is illegal to pass anything but atomic types to ellipsis functions like printf).

                  EDIT:
                  Just so you know, the first example is not possible in C++ prior to C++11.
                  Last edited by nslay; 15 January 2013, 11:14 AM.

                  Comment


                  • #29
                    Originally posted by newwen View Post
                    It seems you're describing Vala.
                    Vala doesn't generate code for its "generics".


                    Also, as to "printf() vs cout", that just seems like such a stupid and unnecessary discussion.
                    You're not discussing languages, only conventions of standard libraries at best. Also,
                    who said you're forced to use std::cout in C++? Just use printf instead if that's what you like.
                    I, personally, prefer my "qDebug() << object" because it's very fast to write and easy to read.
                    Compare that to
                    Code:
                    char *s = object_as_string(object);
                    printf("Object: %s\n", s);
                    free(s);
                    As soon as types get more complex, printf fails miserably.

                    Comment


                    • #30
                      Originally posted by F i L View Post
                      Not really. The language could be fully strong-typed, but also support typeless parameters. The compiler would analyse the function, and determine the restrictions each typeless parameter required, then give compiler errors if the code tries to pass a variable which doesn't meet those restrictions. These functions would be template function, which new version being compiled out with each unique set of param types used (therefor, special restrictions would be required for key Shared Object functions). eg:

                      Code:
                      func foo(x, y:int) # 'x' is typeless, 'y' must be an int
                      {
                          return x * y
                      }
                      
                      func main
                      {
                          var s = "text" # 's' is text
                          var i = 0      # 'i' is an int
                          var f = 0.0    # 'f' is a float
                      
                          var r = foo(s, i) # error: can't pass text as a first parameter
                          var r = foo(f, i) # works: because compiler can multiply a 'int' and a 'float'
                          var r = foo(i, i) # works: compiler can compile 'int' and 'int'
                      }
                      In the code above, two versions of 'foo' would be compiled out... one taking in a (float, int), and one taking in an (int, int).
                      C++(11) does *exactly* this:
                      Code:
                      template<typename T>
                      T foo(const T x &, const int y) 
                      {
                          return x * y;
                      }
                      
                      int main()
                      {
                          char  s[] = "text"; # 's' is text
                          auto i = 0;      # 'i' is an int
                          auto f = 0.0 ;   # 'f' is a float
                      
                          auto r = foo(s, i) ;# error: can't pass text as a first parameter
                          auto r2 = foo(f, i) ;# works: because compiler can multiply a 'int' and a 'float'
                          auto r3 = foo(i, i) ;# works: compiler can compile 'int' and 'int'
                          return 0;
                      }

                      Comment

                      Working...
                      X