C/C++ merger

This is a discussion on C/C++ merger within the C++ Programming forums, part of the General Programming Boards category; Originally Posted by whoie I'll stop, but hating something useful and productive just because it "isn't C++" is being that ...

  1. #46
    and the hat of sweating
    Join Date
    Aug 2007
    Location
    Toronto, ON
    Posts
    3,545
    Quote Originally Posted by whoie View Post
    I'll stop, but hating something useful and productive just because it "isn't C++" is being that which I shall not speak of. If C++ has changed its design goals, then perhaps you are right, it isn't C++ anymore. But one of the goals was so that a programmer wouldn't want to "drop down" to C because it offered something C++ didn't. DI isn't strictly necessary, I agree with that, but it is very useful. If I am writing a decoder of some sort, non-volatile lookup tables become very central to the program. In those types of applications, DI can be a huge bonus. Is C++ supposed to be a language for systems programming, or not?
    Maybe part of the reason why so many C++ programmers couldn't care one way or the other about DI is because it's not something that every programmer will actually use. I'm not sure what type of programming you do (probably very low level), but whatever it is, it sounds like DI is something that you would use quite often. On the other hand, I don't usually do really low level system programming, so I don't think I've ever had a need for DI.
    Another point against adding DI to C++ that I can think of is that it just adds one more piece of syntax that C++ developers would need to learn; either that or they'd be scratching their heads if and when they ever encountered it in someone's code.
    "I am probably the laziest programmer on the planet, a fact with which anyone who has ever seen my code will agree." - esbo, 11/15/2008

    "the internet is a scary place to be thats why i dont use it much." - billet, 03/17/2010

  2. #47
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Portugal
    Posts
    7,434
    // C++
    Code:
    const int a[] = {0,0,0,0,0,0,3,0,0,0,0,42,0,0,0,0};
    
    // Not C++
    const int a[16] = { [6] = 3, [11] = 42 };
    And I fail to see what is so "Not C++" about that.
    Well, a few arguments of my own:

    - DI syntax is alien to C++. A bracket pair inside an initialization list is novelty and I'm unsure of the complications this can have with the proposed changes to the initialization of sequential containers with initialization lists in C++0x. But being a novely is not necessarily bad (personally as I said before I particularly dislike the syntax. But that's me), if the advantages are important to justify such an inclusion. But there's also...

    - The validity of the second definition is questionable under C++ current array initialization rules of its elements. A 0-initialized element under these rules is only possible to the right of the last initializer. I'm unsure of the consequences of changing this rule to now allow 0-initialized elements in the middle of the array. Oddly enough, DI doesn't offer the possibility of specifying a range of elements, consequentely the burden is all on C++ to change its array initialization rules with possible backward compatibilty consequences(?)

    - DI lack of breath seems to me a big point against its adoption in C++. It only offers any help to 0-initialized arrays as you describe. If 1 is needed instead of 0, DI can't do a thing. It's useful on some circumstances, but not useful on many.

    EDIT: Meanwhile there's nothing stopping you from using DI in a C++ program as we speak.
    Last edited by Mario F.; 09-09-2008 at 02:47 PM.
    The programmer’s wife tells him: “Run to the store and pick up a loaf of bread. If they have eggs, get a dozen.”
    The programmer comes home with 12 loaves of bread.


    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  3. #48
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,893
    A 0-initialized element under these rules is only possible to the right of the last initializer. I'm unsure of the consequences of changing this rule to now allow 0-initialized elements in the middle of the array.
    Why would there be consequences?

    Meanwhile there's nothing stopping you from using DI in a C++ program as we speak.
    How about lack of compiler support?

    Anyway, I think the most important argument against DI in C++ is that there's no way to reasonably represent it in the new initializer list functionality. So again there would be an imbalance in the capabilities in initializers of simple and complex types.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  4. #49
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Portugal
    Posts
    7,434
    Quote Originally Posted by CornedBee View Post
    How about lack of compiler support?
    Sure. If your compiler doesn't support C99.

    Anyway, I think the most important argument against DI in C++ is that there's no way to reasonably represent it in the new initializer list functionality.
    Yes, i did mention this point. You just decided to say it anyway.
    The programmer’s wife tells him: “Run to the store and pick up a loaf of bread. If they have eggs, get a dozen.”
    The programmer comes home with 12 loaves of bread.


    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  5. #50
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,893
    Sure. If your compiler doesn't support C99.
    Or doesn't support all C99 features in C++. For example, GCC uses different parsers for C++ and C - it wouldn't be unreasonable at all if DI didn't work in C++, even though the C compiler supports it.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  6. #51
    Registered User
    Join Date
    Jul 2003
    Posts
    110
    Quote Originally Posted by CornedBee View Post
    Anyway, I think the most important argument against DI in C++ is that there's no way to reasonably represent it in the new initializer list functionality. So again there would be an imbalance in the capabilities in initializers of simple and complex types.
    Not if I'm reading this correctly:

    We propose to retain the slightly more restrictive rule “never use aggregate initialization
    if a constructor is declared”. Without this restriction, we would not be able to enforce
    invariants by defining constructors. Consequently, we consider this restriction necessary
    and get this modified basic rule:

    • If a constructor is declared
      • Look for a sequence constructor and use it if we find a best one; if not
      • Look for a constructor (excluding sequence constructors) and use it if we find a best one;
      • if not, It’s an error
    • If no constructor is declared
      • look to see if we can do traditional aggregate or built-in type initialization;
      • if not, It’s an error
    Case handled, AFAICT. Also, initializer_list is just going to be resolved to a built-in array, at least in concept. There is no conflict with DI here:

    Consider:

    std::vector<double> v = {1, 2, 3.14};

    That’s easily done: std::vector has no sequence constructor (until we add the one
    above), so we try {1, 2, 3.14} as a set of arguments to other constructors, that is, we try
    vector(1,2,3.14). That fails, so all of the examples fail to compile when X is std::vector.

    Now add vector(initializer_list<E>) to vector<E> as shown above. Now, the example
    works. The initializer list {1, 2, 3.14} is interpreted as a temporary constructed like this:

    Code:
    double temp[] = {double(1), double(2), 3.14 } ; 
    initializer_list<double> tmp(temp,sizeof(temp)/sizeof(double)); 
    vector<double> v(tmp);
    That is, the compiler constructs an array containing the initializers converted to the
    desired type (here, double). This array is passed to vector’s sequence constructor as an
    initializer_list. The sequence constructor then copies the values from the array into its
    own data structure for elements.
    From that paper Mario posted upthread, I see nothing there that would be a good argument against designated initializers. Also, after reading the document, I understand where the hatred comes from. The document compares C99's Compound Literals, NOT designated initializers. Mario, again, you are confusing two different things here.

  7. #52
    Registered User
    Join Date
    Jul 2003
    Posts
    110
    Quote Originally Posted by CornedBee View Post
    Or doesn't support all C99 features in C++. For example, GCC uses different parsers for C++ and C - it wouldn't be unreasonable at all if DI didn't work in C++, even though the C compiler supports it.
    Indeed not. My g++ chokes on them, but gcc does not if it isn't in C90 mode. If anyone is still reading this thread, try plugging this through a C99 compiler and a C++ compiler to see if it goes through (avoid the obligatory C criticisms please):

    Code:
    whoie$ cat day.c
    #include <stdio.h>
    #include <time.h>
    
    int main(void) {
      puts("Enter a date as mm/dd/yyyy");
      int month = 0, day = 0, year = 0;
      if ( scanf("%d/%d/%d", &month, &day, &year) != 3 ) return 1;
    
      struct tm timeinfo = {
        .tm_year = year - 1900,
        .tm_mon  = month - 1,
        .tm_mday = day
      };
      mktime(&timeinfo);
    
      char day_name[10] = "";
      strftime(day_name, sizeof day_name, "%A", &timeinfo);
      printf("That's a %s.\n", day_name);
      return 0;
    }
    
    whoie$ g++ -o day -W -Wall day.c
    day.c: In function 'int main()':
    day.c:10: error: expected primary-expression before '.' token
    day.c:11: error: expected primary-expression before '.' token
    day.c:12: error: expected primary-expression before '.' token
    day.c:13: warning: missing initializer for member 'tm::tm_mday'
    day.c:13: warning: missing initializer for member 'tm::tm_mon'
    day.c:13: warning: missing initializer for member 'tm::tm_year'
    day.c:13: warning: missing initializer for member 'tm::tm_wday'
    day.c:13: warning: missing initializer for member 'tm::tm_yday'
    day.c:13: warning: missing initializer for member 'tm::tm_isdst'
    day.c:13: warning: missing initializer for member 'tm::tm_gmtoff'
    day.c:13: warning: missing initializer for member 'tm::tm_zone'
    whoie$ gcc -o day -std=c99 -W -Wall day.c
    whoie$ ./day
    Enter a date as mm/dd/yyyy
    09/10/2008
    That's a Wednesday.
    whoie$
    I'd like to hear about anyone who has a C++ compiler that can compile that successfully.

  8. #53
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,893
    Quote Originally Posted by whoie View Post
    Not if I'm reading this correctly:



    Case handled, AFAICT.
    Only for types that allow classic aggregate initialization. Which is why I call it an imbalance. The purpose of initializer list support in C++0x is to bring the power of initializer lists to complex types (i.e. those that have constructors). This is not met if at the same time the initializer list is extended in a way that is not compatible with this new support. And there's no way to represent designated initializers in the new initializer_list, no way to map them unto constructor parameters.

    Also, initializer_list is just going to be resolved to a built-in array, at least in concept. There is no conflict with DI here:
    The initializer lists with array indices, yes. Not those with field names.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  9. #54
    Registered User
    Join Date
    Jul 2003
    Posts
    110
    Quote Originally Posted by CornedBee View Post
    Only for types that allow classic aggregate initialization. Which is why I call it an imbalance. The purpose of initializer list support in C++0x is to bring the power of initializer lists to complex types (i.e. those that have constructors). This is not met if at the same time the initializer list is extended in a way that is not compatible with this new support. And there's no way to represent designated initializers in the new initializer_list, no way to map them unto constructor parameters.
    But you are forgetting that there are two cases, one for types with declared constructors, and one without (arrays and POD-structs). Also, the initializer_list proposed appears to only handle homogeneous types. Which takes it completely out of play for structures anyway, because structs can contain heterogeneous types. They are not sequence containers. This is right out of that paper:

    Code:
    struct S { int a; double v; }; 
    S s = { 1, 2.7 };
    This has of course always worked and it still does. Its meaning is unchanged: initialize
    the members of s in declaration order with the elements from the initializer list in order,
    etc.
    I'm unconvinced that the proposed initializer_list would work for that anyway:
    Code:
    template<class E> class initializer_list { 
     // representation (a pair of pointers or a pointer plus a length) 
    public: 
     constexpr initializer_list(const E*, const E*); // from [first,last)  
     constexpr initializer_list(const E*, int);  // from [first, first+length)
    The template parameter list is only one type.

    Quote Originally Posted by CornedBee View Post
    The initializer lists with array indices, yes. Not those with field names.
    Again, not in play for POD-structures, unless we can discover a way for initializer_list to accept heterogeneous types. According to the proposal:

    Initializer lists that are used for aggregates and argument lists can be heterogeneous and need rarely be stored in memory.
    But they failed to ever show an example of how that would work for heterogeneous lists. The conceptual interim array (see my 2nd to last post upthread) wouldn't work for that. They left a big hole in their proposal by omitting that IMO.

  10. #55
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,893
    But you are forgetting that there are two cases, one for types with declared constructors, and one without (arrays and POD-structs).
    No, that's my entire point.

    I'm unconvinced that the proposed initializer_list would work for that anyway:
    It doesn't. I'm using initializer_list to refer to all the mechanisms of the protocol. One of those is that this:
    Code:
    struct foo
    {
      foo(int a, float b, std::string c);
    };
    
    foo f = {4, 2.3f, "Hello, World!" };
    results in a call to the constructor with the three given arguments. In other words, for heterogenous records, the initializer list becomes an alternate syntax for constructor calls. However, there is no way to map the designated initializer's field names onto this mechanism.

    But they failed to ever show an example of how that would work for heterogeneous lists.
    There's no such thing as a heterogenous list in C, and not really in C++.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  11. #56
    Registered User
    Join Date
    Jul 2003
    Posts
    110
    Quote Originally Posted by CornedBee View Post
    I'm using initializer_list to refer to all the mechanisms of the protocol. One of those is that this:
    Code:
    struct foo
    {
      foo(int a, float b, std::string c);
    };
    
    foo f = {4, 2.3f, "Hello, World!" };
    results in a call to the constructor with the three given arguments. In other words, for heterogenous records, the initializer list becomes an alternate syntax for constructor calls. However, there is no way to map the designated initializer's field names onto this mechanism.
    Nor should there be. That's why there are two cases. One for types with declared constructors (no DI) and one for types without them (DI). No conflict with arrays (as you said before), and no conflict with POD-structs without declared constructors. DI shouldn't be used for types with declared constructors, and won't be if it is adopted straight from C99, so prohibiting their use for types with declared constructors is the right choice. That is a weak argument against DI.

    Quote Originally Posted by CornedBee View Post
    There's no such thing as a heterogenous list in C, and not really in C++.
    Is that right? {4, 2.3f, "Hello, World!" }? Or are you saying that is not a list in that context or something? I think you knew what I meant.

  12. #57
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,893
    I think you knew what I meant.
    No, actually based on your objection, I have no clue what you mean.

    That's why there are two cases.
    My argument was that, since the initializer list propsal for C++0x strives to unify types with and without constructors, making the distinction was counterproductive. The types with constructors play catch-up - and the moment they do, DI makes those without go ahead again? Not good for consistency.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  13. #58
    Registered User
    Join Date
    Jul 2003
    Posts
    110
    Quote Originally Posted by CornedBee View Post
    No, actually based on your objection, I have no clue what you mean.
    You agreed that initializer_list and DI would not conflict with each other for array indicies, but you claimed that they would conflict with those having field names. I am arguing that initializer_list can't conflict because it can't be used for heterogeneous lists (like structure initializer lists) anyway. Then you said that there was no such thing as heterogeneous lists, even though you posted an example of one. That is why I got confused about what you were saying.

    Quote Originally Posted by CornedBee View Post
    My argument was that, since the initializer list propsal for C++0x strives to unify types with and without constructors, making the distinction was counterproductive. The types with constructors play catch-up - and the moment they do, DI makes those without go ahead again? Not good for consistency.
    I understand your objection here, but I am arguing that the solution proposed only really solves the problem for sequence containers. And DI for arrays would fold in naturally there, so one isn't going to go ahead of the other there. Arrays and sequence containers can both use DI for element initilization, because initializer_list will be using a compiler generated interim array anyway. I know you agreed with that already, but I am just trying to point out a distinction that I see.

    For the case of structures, DI isn't a move ahead by POD-structs over those with constructors IMO. DI and constructors are not tools that perform the exact same jobs. Constructors set the invariant for the type to true, DI simply provides a better way to initialize members. Certainly, you can use constructors to do the same job as DI, but it is nowhere near as scalable as DI is for that. You would have to create a constructor for every possible permutation of members in order to achieve a general solution. DI can be used for construction if all you do is initialize members, but then you open the very real possibility of violating the type's invariant if there is one. I see DI as bringing a very expressive tool into the language that cannot be replaced by initialzer_list as proposed.

    I understand the argument for consistency, and I agree with you that it is important. Even if I agreed that the proposed initializer_list worked out for everything (which I don't), and gave the language consistency here, I don't think it is as important as the benefits that DI brings to it.

  14. #59
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Portugal
    Posts
    7,434
    Quote Originally Posted by whoie View Post
    I don't think it is as important as the benefits that DI brings to it.
    It's the benefits issue that I honestly cannot see... I'd be hard pressed to argue some of the remaining discussion regarding initialization lists as proposed on the new standard. All I can surmise is the apparent difficulty implementing DI at the light of the new model and the issue of inconsistency that can be inferred from the document I linked earlier. If you guys think that is not an issue, or is only a partial issue, very well. I lack the knowledge to discuss it.

    But the benefits... hmmm....

    What is the real benefit of a solution that addresses one case instance. Do you agree for instance that DI would be more useful if it included a description for ranged set of elements? That it could also include the description for different "default" initialization values? In other words, what can DI do for me if I want to have the following?

    Code:
    char foo[12] = {'a', 'a', 'c', 'a', 'a', 'e', 'e', 'e', 'e', 'a', 'a', 'a'};
    // or this,
    int bar[12] = {1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1};
    I argue: as a feature DI scope is too limited to justify an effort in implementing it in C++ if there is bigger fish to fry. I have to agree with you when you say, at the very least for the sake of compatibility it should be implemented. But it wasn't. No going around that.

    It's not the first, neither will it be the last suffering the same fate. But the Committee thrives mostly from community input. And if community input has been so low that I you don't even hear the issue being openly debated, the whole "this is very useful" argument falls flat on its face.
    Last edited by Mario F.; 09-12-2008 at 02:51 PM.
    The programmer’s wife tells him: “Run to the store and pick up a loaf of bread. If they have eggs, get a dozen.”
    The programmer comes home with 12 loaves of bread.


    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  15. #60
    Registered User
    Join Date
    Jul 2003
    Posts
    110
    Quote Originally Posted by Mario F. View Post
    What is the real benefit of a solution that addresses one case instance. Do you agree for instance that DI would be more useful if it included a description for ranged set of elements? That it could also include the description for different "default" initialization values?
    I would argue that the "one case instance", that is, default initialization to zero, is the most common.

    What operator would you use to describe a ranged set of elements though? There is nothing like that now. The closest thing I can think of is something like gcc's range operator which you can use for case statements in switch. e.g., case 0..3: You would have to introduce that operator into the language for ranges. DI for arrays could work with that, but you need the operator first. I agree it would be useful, but it's a different feature. Certainly not an argument against DI.

    What would the description for different "default" initialization values look like?

    My argument is that designated initializers make the language more expressive for initialization of large or complex data structures like structures and arrays. Even better, think of an array of structures containing arrays. Or how about a message catalog mapped to an enumeration of string id's for internationalization (something I had to do very recently). I have already suggested other reasons why I think it's a benefit, and I haven't seen a good argument yet for their exclusion.

    Quote Originally Posted by Mario F. View Post
    In other words, what can DI do for me if I want to have the following?

    Code:
    char foo[12] = {'a', 'a', 'c', 'a', 'a', 'e', 'e', 'e', 'e', 'a', 'a', 'a'};
    // or this,
    int bar[12] = {1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1};
    Not a great deal, of course. However, I think a string literal would be a better initialization technique for the first, and DI actually could be used for the second. These arrays are too short to see much of a benefit, but the second case could be rewritten like this using one of the properties of DI:

    Code:
    int bar[12] = {
      // default initialize the array
      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
      // override defaults for specific elements
      [3] = 0, [4] = 0, [5] = 0
    };
    ...which is hardly earth-shattering, but the element receives it's last specified initializer using DI. No need to count to see if it is correct, you can tell just by looking at it. Again, not tremendously helpful here, I agree, but consider tables like these:

    Code:
    int bar[256] = {                    int bar[256] = {
      1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
      1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
      1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    1,1,1,1,1,1,1,11,1,1,1,1,1,1,1,1,
      1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
      1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    1,1,5,1,1,1,1,1,1,1,1,1,1,1,1,1,
      1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
      1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
      1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
      1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
      1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
      1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    1,1,1,1,1,1,1,1,1,1,1,1,1,1,8,1,
      1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
      1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
      1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
      1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
      1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
      [39] = 11, [66] = 5, [174] = 8    };
    };
    Are they equivalent? Do they meet the "spec", and which one is easier to tell? Which is less tedious to work with?
    Last edited by whoie; 09-14-2008 at 01:33 AM.

Page 4 of 4 FirstFirst 1234
Popular pages Recent additions subscribe to a feed

Similar Threads

  1. problem in file merger program
    By san_crazy in forum C Programming
    Replies: 6
    Last Post: 07-09-2008, 07:52 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21