Problems with .h files, conatants and globals

R

Ruud Baltissen

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
www.Baltissen.org
 
B

Ben Bacarisse

Your post is long, but I want to comment on only one point early to try
to head off too much wasted time:
I have decided to port some Turbo Pascal programs to C/C++.

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
particular:
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 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).

<snip>
 
J

James Kuyper

Hello,


I have decided to port some Turbo Pascal programs to C/C++. Most of

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++.

....
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 = '/';

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.

....
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. ...

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.
... I deleted the 'for' construction but then got
this error: "multiple definition of `chcsep'".

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.
 
R

Rosario1903

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

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
 
G

glen herrmannsfeldt

(snip)
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++.


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

-- glen
 
K

Keith Thompson

James Kuyper said:
On 09/28/2013 06:46 AM, Ruud Baltissen wrote: [...]
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 = '/';

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 '/'

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
int.
 
B

Barry Schwarz

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 both RB_unit and CBM_unit. CBM_unit can call functions from RB_unit as well. In both 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];

If your Pascal array goes fro 0 to coMaxDevice, then your C array will
need coMaxDevice+1 entries.
 
R

Ruud Baltissen

Hello James,

... and that you haven't inserted a header guard in that file.

I have:
#ifndef CBM_UNIT_H_INCLUDED
#define CBM_UNIT_H_INCLUDED
....
#endif

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:
http://stackoverflow.com/questions/2831361/how-can-i-create-c-header-files
http://www.learncpp.com/cpp-tutorial/19-header-files/ and
http://www.tutorialspoint.com/cprogramming/c_header_files.htm
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
 
B

Ben Bacarisse

Ruud Baltissen said:
Hello James,



I have:
#ifndef CBM_UNIT_H_INCLUDED
#define CBM_UNIT_H_INCLUDED
...
#endif

Both your and Keith's solution worked fine. But the weird thing, if I
ommit the guard, things compile fine as well.

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?

<snip>
 

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. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,900
Latest member
Nell636132

Latest Threads

Top