Problems with .h files, conatants and globals

Discussion in 'C Programming' started by Ruud Baltissen, Sep 28, 2013.

  1. Hello,

    I have decided to port some Turbo Pascal programs to C/C++. Most of them are Commodore related, for example: a drive simulator, and should run under DOS. I have developed various units and I have troubles converting them to C.. Not converting the code itself but defining the .h files and using them.

    I have one unit, RB_unit.pas, that contains general functions, for example:deleting the white space at both ends of a string. A second one, CBM_unit.pas only contains Commodore related functions, for example: a function to read a byte from the IEEE bus. The main program can call functions from bothRB_unit and CBM_unit. CBM_unit can call functions from RB_unit as well. Inboth units I declared some global variables. In cbm_unit.h I declared a structure with drive properties. In CBM_unit.pas I declared:
    Device : array[0..coMaxDevice] of ^TEmuDevice;
    I translated this to:
    struct EmuDeviceStr Device[coMaxDevice];

    What did I do more: I declared 'byte' in RB_unit.h. I don't know if I keep it but for the moment I use it to learn how to work with .h files.
    I also declared a constant there: const char chcsep = '/';

    The problems:
    If I write the next text in the main function, everything is allright:

    byte b1, b2;
    struct EmuDeviceStr Device[coMaxDevice];

    for (b1 = 0; b1 < coMaxDevice; b1++)
    for (b2 = 0; b1 < 2; b1++)
    Device[b1].Drv[b2].InUse = False;

    But if I write it in a (now empty) function in CBM_unit.c, I get errors. The first one is that cbm_unit doesn't know 'byte'. Quite logical, because I didn't include rb_unit.h yet. But if I include it, I get several errors. I deleted the 'for' construction but then got this error: "multiple definition of `chcsep'".
    Commenting out this const declaration worked again (except two warnings notusing B1 and b2) but then my question: what do I wrong or where should I declare this const then?

    Adding 'for' construction again produced the several error; both 'coMaxDevice' and 'Device' are undeclared. Both have been declared in cbm_unit.h. So I included cbm_unit.h but that resulted in more "multiple definition" errors :(

    Next problem: I know I have to declare 'struct EmuDeviceStr Device[coMaxDevice];' somewhere outside 'main', IMHO in cbm_unit.c. But if I do I get the error "variably modified 'Device' at file scope".

    I can only say now: PLEASE, HELP !!!

    Many thanks in advance!

    Kind regards, Ruud Baltissen
    Ruud Baltissen, Sep 28, 2013
    1. Advertisements

  2. Your post is long, but I want to comment on only one point early to try
    to head off too much wasted time:
    You need to decide if you are writing C or C++. Most people think that
    unless they uses classes the two are the same, but they aren't. In
    The way C and C++ handle consts, particularly when they are in a header
    file, is different in significant ways. You need to pick C or C++ and
    stick with that (both have active news groups).

    Ben Bacarisse, Sep 28, 2013
    1. Advertisements

  3. Ruud Baltissen

    James Kuyper Guest

    There is no such thing as C/C++. There's C, and there's C++, two
    different languages with a lot of similarities, but also some important
    differences - some of which are relevant to your complaints below. You
    have to choose one of the following options:
    1. port to C
    2. port to C++
    3. write code compatible with both C and C++, with the same meaning in
    both languages. This is not as trivial as it sounds.
    4. Do two different ports: one for C, and one for C++.

    That's a problem. The usual reason for putting something in a header
    file is that it will be needed in multiple different translation units.
    That declaration, presumably at file scope, will create a separate
    definition of an object with external linkage named chcsep in each
    translation unit that #includes that header. That's not allowed, you can
    only have one such object defined in your entire program. You can have
    multiple declarations for such an object, but only one definition.
    Converting that into a non-defining declaration requires adding the
    keyword 'extern', and removing the initialization. As a result, the
    value of chcsep would not be known at compile time. You should use the
    preprocessor instead:

    #define CHCSEP '/'

    In C++, your definition would be fine, because a file scope object that
    is declared const or constexpr, and is not declared 'extern', has
    internal linkage (C++ 3.5p3). Any decent implementation of C++ won't
    waste space storing such an object unless it is used in a way that
    requires the object to actually exist, such as taking it's address.
    Otherwise, it's essentially equivalent to CHCSEP. That's true, in part,
    because an object declared const and initialized with an constant
    expression can itself be used as a constant expression (C++ 5.19p2).
    Neither of those rules apply to C.

    That should be the right solution to this problem. The fact that you got
    error messages implies that there's something else involved that you
    haven't told us about rb_unit.h. You should have given us the exact text
    of the error messages - then we might be able to figure out what the
    real problem is.
    If you got that message at link time, then it's precisely the problem
    that I warned you about above.
    However, if you got that message at compile time, it suggests that
    you're #including rb_unit.h at least twice, and that you haven't
    inserted a header guard in that file.
    James Kuyper, Sep 28, 2013
  4. Ruud Baltissen

    Rosario1903 Guest

    if coMaxDevice>=1 this is equivalent to

    Device[0].Drv[0].InUse = False;
    Device[1].Drv[0].InUse = False;

    there would be something not ok in that loop
    Rosario1903, Sep 28, 2013
  5. (snip)

    5. A program that is partly in C and partly in C++, usually because
    more than one person worked on it.

    -- glen
    glen herrmannsfeldt, Sep 28, 2013
  6. Or:

    enum { chcsep = '/' };

    This is a hack that depends on a couple of quirks of C: that both
    enumerators and character constants are constant expressions of type
    Keith Thompson, Sep 28, 2013
  7. If your Pascal array goes fro 0 to coMaxDevice, then your C array will
    need coMaxDevice+1 entries.
    Barry Schwarz, Sep 29, 2013
  8. Hello Rosario1903,

    I already found out in the bad way. But thanks anyway! :)

    Kind regards, Ruud
    Ruud Baltissen, Sep 29, 2013
  9. Hello Keith, James,

    Thank you very much!

    Kind regards, Ruud
    Ruud Baltissen, Sep 29, 2013
  10. Hello James,

    I have:

    Both your and Keith's solution worked fine. But the weird thing, if I ommit the guard, things compile fine as well.
    I have various books about C and C++ but, with one exception, none mentioned the use of header files. So I turned to Google and ran into this: and
    And if you check them, they all mention the guard, but none of them mention the use of constants, enums, variables, etc. That's why I'm glad forums exist and more, that there are people willing to answer your questions! :)

    Kind regards, Ruud
    Ruud Baltissen, Sep 29, 2013
  11. There are lots of things that the compiler can encounter twice without
    causing any problem: function prototypes, certain "extern" declarations
    (those that are not definitions as well), structure declarations and so
    on. One of these is a #define, but a duplicate enum constant (even if
    it has the same value) isn't one. Did a .h file using the enum trick
    really get through multiple includes with no errors?

    Ben Bacarisse, Sep 29, 2013
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.