views:

449

answers:

3

I’m trying to figure out a way to use nested global structs as a sort of API namespacing for my C library.

Specifically, I want to expose a single Primary ‘namespacing struct,’ that contains other such structs (such as Primary.Secondary), that themselves contain function pointers (Primary.Secondary.a_function()).

I’ve abstracted out the following (relatively) simple example of what I want to do:

main.c:

#include "Primary.h"

int main () {
  Primary.Secondary.a_function();
  return 0;
}

Primary.h:

#if !defined(SECONDARY_H)
# include "Secondary.h"
#endif

struct Primary_struct {
  struct Primary__Secondary_struct  Secondary;
} extern Primary;

Primary.c:

#include "Primary.h"

struct Primary_struct Primary = {
  .Secondary = Primary__Secondary
};

Secondary.h:

struct Primary__Secondary_struct {
  void  (*a_function) (void);
  void  (*another_function) (void);
} extern Primary__Secondary;

Secondary.c:

#include "Secondary.h"

#include <stdio.h>

void  Primary__Secondary__a_function  (void);
void  Primary__Secondary__another_function  (void);

struct Primary__Secondary_struct {
  .a_function       = Primary__Secondary__a_function,
  .another_function = Primary__Secondary__another_function
} extern Primary__Secondary;

void Primary__Secondary__a_function(void) {
  Primary.Secondary.another_function();
}

void Primary__Secondary__another_function(void) {
  printf("run!\n");
}

When I attempt to compile this, I run into the following compiler error:

 > C -O0 Primary.c Secondary.c main.c
Primary.c:3:33: error: initializer element is not a compile-time constant
struct Primary_struct Primary = {
                                ^
1 diagnostic generated.

I should note, ideally, both the Primary and Primary__Secondary variables would be const. I was worried that the added complexity would exacerbate the problem… so for now, I’ve left that aspect out.

The problem seems to be that, for some reason, even when set as const, and containing only elements present at compile time, the Primary__Secondary struct is not a compile-time constant, and thus cannot be stored in another struct at compile-time. I can probably work around this by setting up all of the interfaces at runtime, but… that seems like a really hacky solution. I’m looking for any alternative solutions to this problem, that the more C-fu of you than I can come up with.

(Note: This is related to this question, but is substantially different, and quite a bit more specific.)

+2  A: 

What you're trying can't be done; sorry. Here's a condensed example:

#include <stdio.h>

int a = 5;
int b = a;

int main(int argc, char *argv[])
{
  printf("Hello, world!\n"); 
  return 0;
}

Compiling this code gives the error:

main.c:4: error: initializer element is not constant

Because the compiler doesn't know how to make the assignment int b = a at compile time. It's just the way the language works!

Carl Norum
I *think* I grasp the problem behind this (that sounds like sarcasm. It’s not.)… this question, however, is centered around how to circumvent that problem, while still achieving the same goals.
elliottcable
Do it at runtime, or don't bother. C programmers have been using function name prefixes to solve the namespace problem for years (decades!), it should be fine for you too.
Carl Norum
Section 6.6 of the standard may be of use to you, it explains all about constant expressions.
Carl Norum
PDF link: http://www.open-std.org/JTC1/SC22/wg14/www/docs/n1124.pdf
Carl Norum
+2  A: 

You had some odd notations in your code - I've converted them to a more orthodox form. Also, as a general rule, avoid using double-underscore in names; in C++ this is absolutely necessary. You also need to use a pointer to the embedded structure - then the code will run:

Primary.h

//Primary.h:
#ifndef PRIMARY_H
#define PRIMARY_H

#include "Secondary.h"

struct Primary_struct {
  struct Primary_Secondary_struct *Secondary;
};

extern struct Primary_struct Primary;

#endif // PRIMARY_H

Secondary.h

//Secondary.h:
#ifndef SECONDARY_H
#define SECONDARY_H

struct Primary_Secondary_struct {
  void  (*a_function)(void);
  void  (*another_function)(void);
};

extern struct Primary_Secondary_struct Primary_Secondary;

#endif // SECONDARY_H

Primary.c

//Primary.c:

#include "Primary.h"

struct Primary_struct Primary = {
  .Secondary = &Primary_Secondary
};

Secondary.c

//Secondary.c:

#include "Secondary.h"
#include "Primary.h"
#include <stdio.h>

void Primary_Secondary_a_function(void);
void Primary_Secondary_another_function(void);

struct Primary_Secondary_struct Primary_Secondary = {
  .a_function       = Primary_Secondary_a_function,
  .another_function = Primary_Secondary_another_function
};

void Primary_Secondary_a_function(void) {
  Primary_Secondary.another_function();
  printf("hide!\n");
}

void Primary_Secondary_another_function(void) {
  printf("run!\n");
}

main.c

//main.c:
#include "Primary.h"

int main () {
  Primary.Secondary->a_function();
  return 0;
}

This generates:

run!
hide!
Jonathan Leffler
Thanks! Yeah, forgive the odd notations… I’m obviously new around here d-;So, as for the solution… I didn’t think of this. I was really hoping to stick to non-pointers, such that I could use the `Foo.Bar.gaz()` style… but I suppose `Foo->Bar->gaz()` (if I go with a pointer in one place, I’ll probably make them all pointers, for API consistency) really isn’t so bad.In the meantime, I’ve devised another solution of my own, though it requires runtime operations, which is probably a Bad Thing™… I’m going to post it below.
elliottcable
I should also add, an honest thanks for taking the time to offer a constructive reply, instead of telling me I can’t/shouldn’t do what I’m trying to do. I appreciate your time (-:
elliottcable
A: 

I ended up going with a runtime approach, at least for now. I might try a pointers approach (suggested by Jonathan Leffler above) later on, and see if I end up with a less complex / more comprehensible codebase… but this works for now.

I use clang (and gcc)’s __attribute__((constructor)) extension to set up the structs’ relationships at runtime; the same could be achieved more portably (but less cleanly) with some code in main().

I’d offer a little more explanation, but it’s 4AM here… heh. I’ve spent all day on this >,<

main.c:

#include "Package.h"

int main () {
  Package.One.a_function();
  Package.One.another_function();

  Package.Two.a_function();
  Package.Two.another_function();

  return 0;
}

Package.h:

#define PACKAGE_H

#if !defined(ONE_H)
# include "One.h"
#endif
#if !defined(TWO_H)
# include "Two.h"
#endif

// It seems this is broken, at least in `clang`
// #if __has_feature(attribute_constructor)
# define constructor __attribute__((constructor))
// #endif

struct Package_struct {
  struct Package__One_struct  One;
  struct Package__Two_struct  Two;
};

struct Package_struct extern Package;

Package.c:

#include "Package.h"

struct Package_struct Package = {};

One.h:

#define ONE_H

struct Package__One_struct {
  void  (*a_function)       (void);
  void  (*another_function) (void);
};

struct Package__One_struct extern Package__One;

One.c:

#include "One.h"
#include "Package.h"

#include <stdio.h>

void  Package__One__a_function        (void);
void  Package__One__another_function  (void);

struct Package__One_struct Package__One = {
  .a_function       = Package__One__a_function,
  .another_function = Package__One__another_function
};
void constructor Package__register_One(void) {
  Package.One = Package__One; }

void Package__One__a_function(void) {

  Package.One.another_function();

}

void Package__One__another_function(void) {

  printf("one!\n");

}

Two.h:

#define TWO_H

struct Package__Two_struct {
  void  (*a_function)       (void);
  void  (*another_function) (void);
};

struct Package__Two_struct extern Package__Two;

Two.c:

#include "Two.h"
#include "Package.h"

#include <stdio.h>

void  Package__Two__a_function        (void);
void  Package__Two__another_function  (void);

struct Package__Two_struct Package__Two = {
  .a_function       = Package__Two__a_function,
  .another_function = Package__Two__another_function
};
void constructor Package__register_Two(void) {
  Package.Two = Package__Two; }

void Package__Two__a_function(void) {

  Package.Two.another_function();

}

void Package__Two__another_function(void) {

  printf("two!\n");

}
elliottcable