Forward declaration of static variable

Discussion in 'C Programming' started by Jef Driesen, Aug 23, 2008.

  1. Jef Driesen

    Jef Driesen Guest

    I have the following problem in a C project (but that also needs to
    compile with a C++ compiler). I'm using a virtual function table, that
    looks like this in the header file:

    typedef struct device_t {
    const device_backend_t *backend;
    ...
    } device_t;

    typedef struct device_backend_t {
    int (*read) (device_t *device, /* parameters */);
    int (*write) (device_t *device, /* parameters */);
    ...
    } device_backend_t;

    Now when I want to implement a new backend, I write the necessary
    functions in the source file:

    int mydevice_read (device_t *device, /* parameters */)
    {
    ...
    }

    int mydevice_write (device_t *device, /* parameters */)
    {
    ...
    }

    static const device_backend_t mydevice_backend = {
    mydevice_read,
    mydevice_write,
    ...
    };

    So far no problem, but in a number of those functions, I need to have
    access to the "mydevice_backend" variable. For instance to check whether
    the backend pointer is the correct one. How can I forward declare this
    variable properly?

    When I add

    static const device_backend_t reefnet_sensuspro_device_backend;

    to the top of my source file, it works with the gcc compiler, but I'm
    not sure this is valid according to the C (or C++) standard. It
    certainly doesn't compile with msvc (in C++ mode). It complains about a
    redefinition.

    How can I make this work? I could move the definition of the
    "mydevice_backend" variable to the top of the source file and forward
    declare each function inside the structure, but being able to forward
    declare the variable itself would be much more elegant.
    Jef Driesen, Aug 23, 2008
    #1
    1. Advertising

  2. Jef Driesen <> writes:

    > I have the following problem in a C project (but that also needs to
    > compile with a C++ compiler). I'm using a virtual function table, that
    > looks like this in the header file:
    >
    > typedef struct device_t {
    > const device_backend_t *backend;
    > ...
    > } device_t;
    >
    > typedef struct device_backend_t {
    > int (*read) (device_t *device, /* parameters */);
    > int (*write) (device_t *device, /* parameters */);
    > ...
    > } device_backend_t;
    >
    > Now when I want to implement a new backend, I write the necessary
    > functions in the source file:
    >
    > int mydevice_read (device_t *device, /* parameters */)
    > {
    > ...
    > }
    >
    > int mydevice_write (device_t *device, /* parameters */)
    > {
    > ...
    > }
    >
    > static const device_backend_t mydevice_backend = {
    > mydevice_read,
    > mydevice_write,
    > ...
    > };
    >
    > So far no problem, but in a number of those functions, I need to have
    > access to the "mydevice_backend" variable. For instance to check
    > whether the backend pointer is the correct one. How can I forward
    > declare this variable properly?
    >
    > When I add
    >
    > static const device_backend_t reefnet_sensuspro_device_backend;


    I think you mean

    static const device_backend_t mydevice_backend;

    At least that is the name you use above and below.

    > to the top of my source file, it works with the gcc compiler, but I'm
    > not sure this is valid according to the C (or C++) standard. It
    > certainly doesn't compile with msvc (in C++ mode). It complains about
    > a redefinition.


    You may get a better answer in comp.lang.c++ since this whole area is
    one in which there are subtle but important differences between C and
    C++. If you need the code to pass through a C++ compiler, then you
    are writing C++ (no matter how much it looks like C!) and you need C++
    experts.

    I you had said it failed on MSVC in C mode I would have written: It is
    likely that exact details are required so please construct a minimal
    example that compiles in one but not the other compiler (you are 99%
    there already with what you outlined).

    The reason I say this is that "at the top" is not clear enough for
    this problem because the declaration of the static struct is what is
    called (in C) a tentative definition and special rules apply. Since
    it has internal linkage, it must be a complete type (by the standard)
    but gcc accepts it even when incomplete (it is allowed to -- there is
    no requirement for a diagnostic message).

    [In fact I wrote that before I saw your "in C++ mode" remark. I
    suspect you don't need C's answer to this question.]

    > How can I make this work? I could move the definition of the
    > "mydevice_backend" variable to the top of the source file and forward
    > declare each function inside the structure, but being able to forward
    > declare the variable itself would be much more elegant.


    You can in C, but maybe not in C++. The C++ people can tell you.

    --
    Ben.
    Ben Bacarisse, Aug 24, 2008
    #2
    1. Advertising

  3. Jef Driesen

    Guest

    On Aug 23, 2:57 am, Jef Driesen <>
    wrote:
    > I have the following problem in a C project (but that also needs to
    > compile with a C++ compiler). I'm using a virtual function table, that
    > looks like this in the header file:
    >
    > typedef struct device_t {
    > const device_backend_t *backend;
    > ...
    >
    > } device_t;
    >
    > typedef struct device_backend_t {
    > int (*read) (device_t *device, /* parameters */);
    > int (*write) (device_t *device, /* parameters */);
    > ...
    >
    > } device_backend_t;


    .....

    > So far no problem, but in a number of those functions, I need to have
    > access to the "mydevice_backend" variable. For instance to check whether
    > the backend pointer is the correct one. How can I forward declare this
    > variable properly?
    >
    > When I add
    >
    > static const device_backend_t reefnet_sensuspro_device_backend;
    >
    > to the top of my source file, it works with the gcc compiler, but I'm
    > not sure this is valid according to the C (or C++) standard. It
    > certainly doesn't compile with msvc (in C++ mode). It complains about a
    > redefinition.


    Just to be clear, does g++ accept that, or just gcc?

    In C(99), that is a tentative definition; it can use a later
    initializer to do the actual initialization. [C99 standard: 6.9.2
    paragraph 2] Tentative definitions were intentionally removed from C+
    +98 [Informative Annex C.2.2 paragraph 1]; this indeed is a
    redefinition in C++.

    From what you describe, I don't think extern would be a good idea
    (that should work for forward-declaring a file-scope variable in both
    C and C++).

    > How can I make this work? I could move the definition of the
    > "mydevice_backend" variable to the top of the source file and forward
    > declare each function inside the structure, but being able to forward
    > declare the variable itself would be much more elegant.


    Assuming static is the correct linkage, I'd just forward-declare the
    functions to be portable.
    , Aug 24, 2008
    #3
  4. Jef Driesen

    Jef Driesen Guest

    Ben Bacarisse wrote:
    > Jef Driesen <> writes:
    >
    >> I have the following problem in a C project (but that also needs to
    >> compile with a C++ compiler). I'm using a virtual function table, that
    >> looks like this in the header file:
    >>
    >> typedef struct device_t {
    >> const device_backend_t *backend;
    >> ...
    >> } device_t;
    >>
    >> typedef struct device_backend_t {
    >> int (*read) (device_t *device, /* parameters */);
    >> int (*write) (device_t *device, /* parameters */);
    >> ...
    >> } device_backend_t;
    >>
    >> Now when I want to implement a new backend, I write the necessary
    >> functions in the source file:
    >>
    >> int mydevice_read (device_t *device, /* parameters */)
    >> {
    >> ...
    >> }
    >>
    >> int mydevice_write (device_t *device, /* parameters */)
    >> {
    >> ...
    >> }
    >>
    >> static const device_backend_t mydevice_backend = {
    >> mydevice_read,
    >> mydevice_write,
    >> ...
    >> };
    >>
    >> So far no problem, but in a number of those functions, I need to have
    >> access to the "mydevice_backend" variable. For instance to check
    >> whether the backend pointer is the correct one. How can I forward
    >> declare this variable properly?
    >>
    >> When I add
    >>
    >> static const device_backend_t reefnet_sensuspro_device_backend;

    >
    > I think you mean
    >
    > static const device_backend_t mydevice_backend;
    >
    > At least that is the name you use above and below.


    You're correct. This was a copy-and-paste error.

    >> to the top of my source file, it works with the gcc compiler, but I'm
    >> not sure this is valid according to the C (or C++) standard. It
    >> certainly doesn't compile with msvc (in C++ mode). It complains about
    >> a redefinition.

    >
    > You may get a better answer in comp.lang.c++ since this whole area is
    > one in which there are subtle but important differences between C and
    > C++. If you need the code to pass through a C++ compiler, then you
    > are writing C++ (no matter how much it looks like C!) and you need C++
    > experts.
    >
    > I you had said it failed on MSVC in C mode I would have written: It is
    > likely that exact details are required so please construct a minimal
    > example that compiles in one but not the other compiler (you are 99%
    > there already with what you outlined).
    >
    > The reason I say this is that "at the top" is not clear enough for
    > this problem because the declaration of the static struct is what is
    > called (in C) a tentative definition and special rules apply. Since
    > it has internal linkage, it must be a complete type (by the standard)
    > but gcc accepts it even when incomplete (it is allowed to -- there is
    > no requirement for a diagnostic message).


    With "at the top" I meant after the definition of device_t and
    device_backend_t types, but right before the mydevice_read,
    mydevice_write functions.

    > [In fact I wrote that before I saw your "in C++ mode" remark. I
    > suspect you don't need C's answer to this question.]


    The C++ mode is only used for compiling in msvc, because I'm using a
    number of C99 features (variable declarations anywere in a code block),
    that are not supported in the msvc C compiler. In linux, I am compiling
    in C99 mode with gcc.

    >> How can I make this work? I could move the definition of the
    >> "mydevice_backend" variable to the top of the source file and forward
    >> declare each function inside the structure, but being able to forward
    >> declare the variable itself would be much more elegant.

    >
    > You can in C, but maybe not in C++. The C++ people can tell you.
    Jef Driesen, Aug 24, 2008
    #4
  5. Jef Driesen

    Jef Driesen Guest

    wrote:
    > On Aug 23, 2:57 am, Jef Driesen <>
    > wrote:
    >> I have the following problem in a C project (but that also needs to
    >> compile with a C++ compiler). I'm using a virtual function table, that
    >> looks like this in the header file:
    >>
    >> typedef struct device_t {
    >> const device_backend_t *backend;
    >> ...
    >>
    >> } device_t;
    >>
    >> typedef struct device_backend_t {
    >> int (*read) (device_t *device, /* parameters */);
    >> int (*write) (device_t *device, /* parameters */);
    >> ...
    >>
    >> } device_backend_t;

    >
    > ....
    >
    >> So far no problem, but in a number of those functions, I need to have
    >> access to the "mydevice_backend" variable. For instance to check whether
    >> the backend pointer is the correct one. How can I forward declare this
    >> variable properly?
    >>
    >> When I add
    >>
    >> static const device_backend_t mydevice_backend;
    >>
    >> to the top of my source file, it works with the gcc compiler, but I'm
    >> not sure this is valid according to the C (or C++) standard. It
    >> certainly doesn't compile with msvc (in C++ mode). It complains about a
    >> redefinition.

    >
    > Just to be clear, does g++ accept that, or just gcc?


    gcc in C99 mode. I didn't try with g++ yet.

    > In C(99), that is a tentative definition; it can use a later
    > initializer to do the actual initialization. [C99 standard: 6.9.2
    > paragraph 2] Tentative definitions were intentionally removed from C+
    > +98 [Informative Annex C.2.2 paragraph 1]; this indeed is a
    > redefinition in C++.
    >
    > From what you describe, I don't think extern would be a good idea
    > (that should work for forward-declaring a file-scope variable in both
    > C and C++).


    How would I use extern here?

    >> How can I make this work? I could move the definition of the
    >> "mydevice_backend" variable to the top of the source file and forward
    >> declare each function inside the structure, but being able to forward
    >> declare the variable itself would be much more elegant.

    >
    > Assuming static is the correct linkage, I'd just forward-declare the
    > functions to be portable.


    Static is correct. The mydevice_backend variable only needs to be
    "visible" at file-scope.
    Jef Driesen, Aug 24, 2008
    #5
  6. On Sat, 23 Aug 2008 09:57:31 +0200, Jef Driesen
    <> wrote:

    >I have the following problem in a C project (but that also needs to
    >compile with a C++ compiler). I'm using a virtual function table, that


    Do you mean you are writing a C++ program this will be used by a
    project that is mostly in C or do you need a program that compiles
    correctly in both? I will assume the latter. In spite of the
    similarity in the language names and a good deal of common syntax, the
    two are separate languages. Trying to write code that will compile in
    both will force you to limit yourself to the common subset and will
    also require you to make choices which are undesirable in at least one
    of the languages.

    >looks like this in the header file:
    >
    >typedef struct device_t {
    > const device_backend_t *backend;


    In C, the structure tag is not a type. Before this typedef, add the
    "incomplete" typedef
    typedef struct device_backend_t device_backend_t;
    so that the type device_backend_t is known and

    > ...
    >} device_t;
    >
    >typedef struct device_backend_t {


    then change this typedef to a simple structure declaration.

    > int (*read) (device_t *device, /* parameters */);
    > int (*write) (device_t *device, /* parameters */);
    > ...
    >} device_backend_t;
    >
    >Now when I want to implement a new backend, I write the necessary
    >functions in the source file:
    >
    >int mydevice_read (device_t *device, /* parameters */)
    >{
    > ...
    >}
    >
    >int mydevice_write (device_t *device, /* parameters */)
    >{
    > ...
    >}
    >
    >static const device_backend_t mydevice_backend = {
    > mydevice_read,
    > mydevice_write,
    > ...
    >};
    >
    >So far no problem, but in a number of those functions, I need to have
    >access to the "mydevice_backend" variable. For instance to check whether
    >the backend pointer is the correct one. How can I forward declare this
    >variable properly?
    >
    >When I add
    >
    >static const device_backend_t reefnet_sensuspro_device_backend;
    >
    >to the top of my source file, it works with the gcc compiler, but I'm
    >not sure this is valid according to the C (or C++) standard. It


    Defining a static variable a file scope is valid. As with any
    "global" variable, it is visible to every function within the source
    file and exists for the life of the program. Since it is static, it
    has internal linkage which means the name of the variable will not be
    exported to the linker. If another source file uses the same variable
    name, it will represent a different variable. Since you don't provide
    initialization for this object with static duration, it will
    automatically be initialized to the appropriate form of zero. As the
    structure contains two pointers, they will both be set to NULL. But
    because you declare the structure as const, you will not be able to
    change the value of either pointer.

    >certainly doesn't compile with msvc (in C++ mode). It complains about a
    >redefinition.


    Since the only thing being defined is the object named
    reefnet...backend, you must have another definition of that object in
    code that precedes this definition. It could be in a header (which
    should never have definitions anyway).

    >
    >How can I make this work? I could move the definition of the


    What move? Your previous paragraph said it was already at the top.
    Where is it really?

    >"mydevice_backend" variable to the top of the source file and forward
    >declare each function inside the structure, but being able to forward
    >declare the variable itself would be much more elegant.


    If you put your struct declarations/typedefs and prototypes for each
    function in a header and include the header before any of your
    definitions, you can define this object at the top of your code and
    initialize it to point to the functions you want.

    file.h:
    typedef struct device_backend_t device_backend_t;

    typedef struct device_t {
    const device_backend_t *backend;
    ...
    } device_t;

    struct device_backend_t {
    int (*read) (device_t *device, /* parameters */);
    int (*write) (device_t *device, /* parameters */);
    ...
    };

    int mydevice_read (device_t *device, /* parameters */);

    int mydevice_write (device_t *device, /* parameters */);

    --------------------------------------------------------------

    file.c:
    #include "file.h"

    static const device_backend_t mydevice_backend = {
    mydevice_read,
    mydevice_write,
    ...
    };

    static const device_backend_t reefnet_sensuspro_device_backend =
    {...};

    main and mydevice_read etc to follow.

    --
    Remove del for email
    Barry Schwarz, Aug 24, 2008
    #6
  7. Jef Driesen <> writes:

    > Ben Bacarisse wrote:

    <snip>
    >> The reason I say this is that "at the top" is not clear enough for
    >> this problem because the declaration of the static struct is what is
    >> called (in C) a tentative definition and special rules apply. Since
    >> it has internal linkage, it must be a complete type (by the standard)
    >> but gcc accepts it even when incomplete (it is allowed to -- there is
    >> no requirement for a diagnostic message).

    >
    > With "at the top" I meant after the definition of device_t and
    > device_backend_t types, but right before the mydevice_read,
    > mydevice_write functions.


    OK, then at that point the type is not incomplete and a C compiler
    should take that as a tentative defintions -- one that gets "rounded
    out" by the later details. I understand that this is true in C90 as
    well as in C99 (I am less sure of some details of C90). I.e. if this
    were all you were doing, you would not need to push MSVC into C++ mode
    since it complies (with the right switches) to C90.

    >> [In fact I wrote that before I saw your "in C++ mode" remark. I
    >> suspect you don't need C's answer to this question.]

    >
    > The C++ mode is only used for compiling in msvc, because I'm using a
    > number of C99 features (variable declarations anywere in a code
    > block), that are not supported in the msvc C compiler. In linux, I am
    > compiling in C99 mode with gcc.


    I see. Other parts of the code require more than MSVC C offers. It
    seems daft that MSVC does not have a "near" C99 mode, but that is the
    way the world is. As soon as you take C++ sematics, const changes and
    tentative definitions are lost. Is another compiler an option for you
    Windows build?

    If you do ask in comp.lang.c++ I suspect the answers will involve
    using more C++ features since, having just had a peek, I see the C++
    standard is very clear that tentative definitions are out.

    [Ah! I see this is now cross-posted so my assertions about C++ will
    be tested -- just ignore the phrases "if you ask in...".]

    --
    Ben.
    Ben Bacarisse, Aug 24, 2008
    #7
  8. Jef Driesen

    Guest

    On Aug 24, 1:34 am, Jef Driesen <>
    wrote:
    > wrote:
    > > On Aug 23, 2:57 am, Jef Driesen <>
    > > wrote:
    > >> I have the following problem in a C project (but that also needs to
    > >> compile with a C++ compiler). I'm using a virtual function table, that
    > >> looks like this in the header file:

    >
    > >> typedef struct device_t {
    > >> const device_backend_t *backend;
    > >> ...

    >
    > >> } device_t;

    >
    > >> typedef struct device_backend_t {
    > >> int (*read) (device_t *device, /* parameters */);
    > >> int (*write) (device_t *device, /* parameters */);
    > >> ...

    >
    > >> } device_backend_t;

    >
    > > ....

    >
    > >> So far no problem, but in a number of those functions, I need to have
    > >> access to the "mydevice_backend" variable. For instance to check whether
    > >> the backend pointer is the correct one. How can I forward declare this
    > >> variable properly?

    >
    > >> When I add

    >
    > >> static const device_backend_t mydevice_backend;

    >
    > >> to the top of my source file, it works with the gcc compiler, but I'm
    > >> not sure this is valid according to the C (or C++) standard. It
    > >> certainly doesn't compile with msvc (in C++ mode). It complains about a
    > >> redefinition.

    >
    > > Just to be clear, does g++ accept that, or just gcc?

    >
    > gcc in C99 mode. I didn't try with g++ yet.
    >
    > > In C(99), that is a tentative definition; it can use a later
    > > initializer to do the actual initialization. [C99 standard: 6.9.2
    > > paragraph 2] Tentative definitions were intentionally removed from C+
    > > +98 [Informative Annex C.2.2 paragraph 1]; this indeed is a
    > > redefinition in C++.

    >
    > > From what you describe, I don't think extern would be a good idea
    > > (that should work for forward-declaring a file-scope variable in both
    > > C and C++).

    >
    > How would I use extern here?


    Since the full definition is later on in the same file, use

    extern const device_backend_t mydevice_backend;

    to get the forward declaration in both C and C++. Unfortunately,
    locking down an extern declaration to file scope is feasible only in C+
    + (wrap both the forward declaration, and the actual definition, in an
    anonymous namespace).

    If your coding standards tolerate the C preprocessor, something like
    this (subject to actual compile testing not done here) would work by
    giving each language what it wants:

    #ifdef __cplusplus
    namespace {
    extern const device_backend_t mydevice_backend;
    }
    #else
    static const device_backend_t mydevice_backend;
    #endif

    (also using the preprocessor to wrap the actual definition in an
    anonymous namespace for C++.)
    , Aug 24, 2008
    #8
  9. Jef Driesen

    Jef Driesen Guest

    Barry Schwarz wrote:
    > On Sat, 23 Aug 2008 09:57:31 +0200, Jef Driesen
    > <> wrote:
    >
    >> I have the following problem in a C project (but that also needs to
    >> compile with a C++ compiler). I'm using a virtual function table, that

    >
    > Do you mean you are writing a C++ program this will be used by a
    > project that is mostly in C or do you need a program that compiles
    > correctly in both? I will assume the latter. In spite of the
    > similarity in the language names and a good deal of common syntax, the
    > two are separate languages. Trying to write code that will compile in
    > both will force you to limit yourself to the common subset and will
    > also require you to make choices which are undesirable in at least one
    > of the languages.


    It's a C library. The reason why I would like to be able to compile it
    with a C++ compiler is that I'm using a number of C99 features (variable
    declarations anywhere in a code block). Unfortunately the msvc C
    compiler does not support C99, but those features are supported in C++,
    so I'm trying to make it build in C++ mode.

    >> looks like this in the header file:
    >>
    >> typedef struct device_t {
    >> const device_backend_t *backend;

    >
    > In C, the structure tag is not a type. Before this typedef, add the
    > "incomplete" typedef
    > typedef struct device_backend_t device_backend_t;
    > so that the type device_backend_t is known and
    >
    >> ...
    >> } device_t;
    >>
    >> typedef struct device_backend_t {

    >
    > then change this typedef to a simple structure declaration.


    I'm doing it like that in my source code. I omitted the forward
    declarations in this post to make it shorter.

    >> int (*read) (device_t *device, /* parameters */);
    >> int (*write) (device_t *device, /* parameters */);
    >> ...
    >> } device_backend_t;
    >>
    >> Now when I want to implement a new backend, I write the necessary
    >> functions in the source file:
    >>
    >> int mydevice_read (device_t *device, /* parameters */)
    >> {
    >> ...
    >> }
    >>
    >> int mydevice_write (device_t *device, /* parameters */)
    >> {
    >> ...
    >> }
    >>
    >> static const device_backend_t mydevice_backend = {
    >> mydevice_read,
    >> mydevice_write,
    >> ...
    >> };
    >>
    >> So far no problem, but in a number of those functions, I need to have
    >> access to the "mydevice_backend" variable. For instance to check whether
    >> the backend pointer is the correct one. How can I forward declare this
    >> variable properly?
    >>
    >> When I add
    >>
    >> static const device_backend_t reefnet_sensuspro_device_backend;
    >>
    >> to the top of my source file, it works with the gcc compiler, but I'm
    >> not sure this is valid according to the C (or C++) standard. It

    >
    > Defining a static variable a file scope is valid. As with any
    > "global" variable, it is visible to every function within the source
    > file and exists for the life of the program. Since it is static, it
    > has internal linkage which means the name of the variable will not be
    > exported to the linker. If another source file uses the same variable
    > name, it will represent a different variable. Since you don't provide
    > initialization for this object with static duration, it will
    > automatically be initialized to the appropriate form of zero. As the
    > structure contains two pointers, they will both be set to NULL. But
    > because you declare the structure as const, you will not be able to
    > change the value of either pointer.
    >
    >> certainly doesn't compile with msvc (in C++ mode). It complains about a
    >> redefinition.

    >
    > Since the only thing being defined is the object named
    > reefnet...backend, you must have another definition of that object in
    > code that precedes this definition. It could be in a header (which
    > should never have definitions anyway).


    Sorry, that was a copy-and-paste error. The variable was supposed to be
    named "mydevice_backend".

    >> How can I make this work? I could move the definition of the

    >
    > What move? Your previous paragraph said it was already at the top.
    > Where is it really?
    >
    >> "mydevice_backend" variable to the top of the source file and forward
    >> declare each function inside the structure, but being able to forward
    >> declare the variable itself would be much more elegant.


    I only have a declaration (without any initialization) at the top. The
    initialization is done after the implementation of the mydevice_*
    functions. So the file looks like this:

    static const device_backend_t mydevice_backend;

    int mydevice_read (device_t *device, /* parameters */)
    {
    ...
    }

    int mydevice_write (device_t *device, /* parameters */)
    {
    ...
    }

    static const device_backend_t mydevice_backend = {
    mydevice_read,
    mydevice_write,
    ...
    };

    With "moving to the top", I meant forward declaring the functions at the
    top of the file, so the initialization can be done at the top as well.
    Now will look like this:

    int mydevice_read (device_t *device, /* parameters */);
    int mydevice_write (device_t *device, /* parameters */);

    static const device_backend_t mydevice_backend = {
    mydevice_read,
    mydevice_write,
    ...
    };

    int mydevice_read (device_t *device, /* parameters */)
    {
    ...
    }

    int mydevice_write (device_t *device, /* parameters */)
    {
    ...
    }

    But in that case I have to forward declare every single function that is
    listed in the virtual function table, while all I need would be a
    forward declaration of the "mydevice_backend" variable.

    > If you put your struct declarations/typedefs and prototypes for each
    > function in a header and include the header before any of your
    > definitions, you can define this object at the top of your code and
    > initialize it to point to the functions you want.
    >
    > file.h:
    > typedef struct device_backend_t device_backend_t;
    >
    > typedef struct device_t {
    > const device_backend_t *backend;
    > ...
    > } device_t;
    >
    > struct device_backend_t {
    > int (*read) (device_t *device, /* parameters */);
    > int (*write) (device_t *device, /* parameters */);
    > ...
    > };
    >
    > int mydevice_read (device_t *device, /* parameters */);
    >
    > int mydevice_write (device_t *device, /* parameters */);
    >
    > --------------------------------------------------------------
    >
    > file.c:
    > #include "file.h"
    >
    > static const device_backend_t mydevice_backend = {
    > mydevice_read,
    > mydevice_write,
    > ...
    > };
    >
    > static const device_backend_t reefnet_sensuspro_device_backend =
    > {...};
    >
    > main and mydevice_read etc to follow.


    That is essentially the same as what I wrote above, except that I'm not
    using a header file. Because this is all "private" and used only inside
    the c file. My header file contains only the "public" part of the api.
    Jef Driesen, Aug 25, 2008
    #9
  10. Jef Driesen

    Ian Collins Guest

    Jef Driesen wrote:
    > Barry Schwarz wrote:
    >> On Sat, 23 Aug 2008 09:57:31 +0200, Jef Driesen
    >> <> wrote:
    >>
    >>> I have the following problem in a C project (but that also needs to
    >>> compile with a C++ compiler). I'm using a virtual function table, that

    >>
    >> Do you mean you are writing a C++ program this will be used by a
    >> project that is mostly in C or do you need a program that compiles
    >> correctly in both? I will assume the latter. In spite of the
    >> similarity in the language names and a good deal of common syntax, the
    >> two are separate languages. Trying to write code that will compile in
    >> both will force you to limit yourself to the common subset and will
    >> also require you to make choices which are undesirable in at least one
    >> of the languages.

    >
    > It's a C library. The reason why I would like to be able to compile it
    > with a C++ compiler is that I'm using a number of C99 features (variable
    > declarations anywhere in a code block). Unfortunately the msvc C
    > compiler does not support C99, but those features are supported in C++,
    > so I'm trying to make it build in C++ mode.
    >

    So it's now a C++ library, so you may as well update the code accordingly.

    --
    Ian Collins.
    Ian Collins, Aug 25, 2008
    #10
  11. On Mon, 25 Aug 2008 10:02:02 +0200, Jef Driesen
    <> wrote:

    >Barry Schwarz wrote:
    >> On Sat, 23 Aug 2008 09:57:31 +0200, Jef Driesen
    >> <> wrote:
    >>
    >>> I have the following problem in a C project (but that also needs to
    >>> compile with a C++ compiler). I'm using a virtual function table, that


    snip

    >>> Now when I want to implement a new backend, I write the necessary
    >>> functions in the source file:
    >>>
    >>> int mydevice_read (device_t *device, /* parameters */)
    >>> {
    >>> ...
    >>> }
    >>>
    >>> int mydevice_write (device_t *device, /* parameters */)
    >>> {
    >>> ...
    >>> }
    >>>
    >>> static const device_backend_t mydevice_backend = {
    >>> mydevice_read,
    >>> mydevice_write,
    >>> ...
    >>> };
    >>>
    >>> So far no problem, but in a number of those functions, I need to have
    >>> access to the "mydevice_backend" variable. For instance to check whether
    >>> the backend pointer is the correct one. How can I forward declare this
    >>> variable properly?
    >>>
    >>> When I add
    >>>
    >>> static const device_backend_t reefnet_sensuspro_device_backend;
    >>>
    >>> to the top of my source file, it works with the gcc compiler, but I'm
    >>> not sure this is valid according to the C (or C++) standard. It

    >>

    snip

    >I only have a declaration (without any initialization) at the top. The
    >initialization is done after the implementation of the mydevice_*
    >functions. So the file looks like this:
    >
    >static const device_backend_t mydevice_backend;


    This is a definition because static object have an implied
    initialization if one is not provided.

    >
    >int mydevice_read (device_t *device, /* parameters */)
    >{
    > ...
    >}
    >
    >int mydevice_write (device_t *device, /* parameters */)
    >{
    > ...
    >}
    >
    >static const device_backend_t mydevice_backend = {
    > mydevice_read,
    > mydevice_write,
    > ...
    >};


    And then this very obviously is a duplicate definition.

    >
    >With "moving to the top", I meant forward declaring the functions at the
    >top of the file, so the initialization can be done at the top as well.
    >Now will look like this:
    >
    >int mydevice_read (device_t *device, /* parameters */);
    >int mydevice_write (device_t *device, /* parameters */);
    >
    >static const device_backend_t mydevice_backend = {
    > mydevice_read,
    > mydevice_write,
    > ...
    >};
    >
    >int mydevice_read (device_t *device, /* parameters */)
    >{
    > ...
    >}
    >
    >int mydevice_write (device_t *device, /* parameters */)
    >{
    > ...
    >}
    >
    >But in that case I have to forward declare every single function that is
    >listed in the virtual function table, while all I need would be a


    Yes. But it is a simple cut and paste since you can use the same
    first line(s) from the definition for your prototype. Just add a
    semicolon.

    >forward declaration of the "mydevice_backend" variable.


    There is something called a tentative definition. I don't know if the
    rules in C and C++ are sufficiently similar for it to be of value. I
    also don't know if you can have a tentative definition for an object
    with static duration.

    --
    Remove del for email
    Barry Schwarz, Aug 25, 2008
    #11
  12. Jef Driesen

    Guest

    Barry Schwarz wrote:
    > On Mon, 25 Aug 2008 10:02:02 +0200, Jef Driesen
    > <> wrote:
    >
    > >Barry Schwarz wrote:
    > >> On Sat, 23 Aug 2008 09:57:31 +0200, Jef Driesen
    > >> <> wrote:
    > >>
    > >>> I have the following problem in a C project (but that also needs to
    > >>> compile with a C++ compiler). I'm using a virtual function table, that

    ....
    > >I only have a declaration (without any initialization) at the top. The
    > >initialization is done after the implementation of the mydevice_*
    > >functions. So the file looks like this:
    > >
    > >static const device_backend_t mydevice_backend;

    >
    > This is a definition because static object have an implied
    > initialization if one is not provided.


    In C, because this is only a tentative definition, the initialization
    is implicit only if there is no later non-tentative definition of the
    same object with an explicit definition.

    > >int mydevice_read (device_t *device, /* parameters */)
    > >{
    > > ...
    > >}
    > >
    > >int mydevice_write (device_t *device, /* parameters */)
    > >{
    > > ...
    > >}
    > >
    > >static const device_backend_t mydevice_backend = {
    > > mydevice_read,
    > > mydevice_write,
    > > ...
    > >};

    >
    > And then this very obviously is a duplicate definition.
    >
    > >
    > >With "moving to the top", I meant forward declaring the functions at the
    > >top of the file, so the initialization can be done at the top as well.
    > >Now will look like this:
    > >
    > >int mydevice_read (device_t *device, /* parameters */);
    > >int mydevice_write (device_t *device, /* parameters */);
    > >
    > >static const device_backend_t mydevice_backend = {
    > > mydevice_read,
    > > mydevice_write,
    > > ...
    > >};
    > >
    > >int mydevice_read (device_t *device, /* parameters */)
    > >{
    > > ...
    > >}
    > >
    > >int mydevice_write (device_t *device, /* parameters */)
    > >{
    > > ...
    > >}
    > >
    > >But in that case I have to forward declare every single function that is
    > >listed in the virtual function table, while all I need would be a

    >
    > Yes. But it is a simple cut and paste since you can use the same
    > first line(s) from the definition for your prototype. Just add a
    > semicolon.
    >
    > >forward declaration of the "mydevice_backend" variable.

    >
    > There is something called a tentative definition. I don't know if the
    > rules in C and C++ are sufficiently similar for it to be of value. I
    > also don't know if you can have a tentative definition for an object
    > with static duration.


    "A declaration of an identifier for an object that has file scope
    without an initializer, and without a storage-class specifier or with
    the storage-class specifier static, constitutes a tentative
    definition." (6.9.2p2)

    As far as I can see, 'mydevice_backend" does have file scope, does not
    have an (explicit) initializer, and the only strorage-class specifier
    it has is "static", which is explicitly permitted. Therefore, it does
    qualify as a tentative definition in C. As a result, it is perfectly
    legal to follow it up with other tentative definitions, or with a non-
    tentative definition. The different definitions must be of compatible,
    as described in 6.2.7. Since device_backend_t is typedef for a struct
    type, about the only meaningful freedom allowed for the second
    declaration to differ from the first is by providing an initializer.

    The problem is that C++ has no such concept, so code which must
    compile both as C code and as C++ code can't make use of that fact.
    , Aug 25, 2008
    #12
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. qazmlp
    Replies:
    1
    Views:
    599
    Jonathan Turkanis
    Feb 15, 2004
  2. Replies:
    4
    Views:
    1,076
    Richard Tobin
    Dec 12, 2006
  3. John Ratliff
    Replies:
    2
    Views:
    324
    John Ratliff
    Aug 26, 2005
  4. Bolin
    Replies:
    4
    Views:
    408
  5. Jef Driesen
    Replies:
    11
    Views:
    923
Loading...

Share This Page