4

I am creating a bunch of C structs so i can encapsulate data to be passed over a dll c interface. The structs have many members, and I want them to have defaults, so that they can be created with only a few members specified.

As I understand it, the structs need to remain c-style, so can't contain constructors. Whats the best way to create them? I was thinking a factory?

dangerousdave
  • 6,331
  • 8
  • 45
  • 62

4 Answers4

3
struct Foo {
    static Foo make_default ();
};

A factory is overkill. You use it when you want to create instances of a given interface, but the runtime type of the implementation isn't statically known at the site of creation.

spraff
  • 32,570
  • 22
  • 121
  • 229
  • 6
    Surely this _is_ a factory? Albeit a function, not a type? – Lightness Races in Orbit Jan 20 '12 at 14:27
  • As I said above, I dont believe the structs can contain methods and still be used over a c interface. I have tried, and it didn't work. – dangerousdave Jan 20 '12 at 14:28
  • 2
    @dangerousdave: What you want is a [POD](http://stackoverflow.com/questions/146452/what-are-pod-types-in-c), and these are allowed to have `static` members. – Björn Pollex Jan 20 '12 at 14:28
  • They can also have non-static members. Also, [POD has been relaxed](http://en.wikipedia.org/wiki/C%2B%2B11#Modification_to_the_definition_of_plain_old_data). – spraff Jan 20 '12 at 14:31
  • @LightnessRacesinOrbit no, it's not a factory, it's just an ordinary function returning an ordinary object. A factory goes up a step in abstraction -- if it can't return objects of variable type, it doesn't count. – spraff Jan 20 '12 at 14:33
  • 2
    @spraff: that's an *abstract* factory. – R. Martinho Fernandes Jan 20 '12 at 14:36
  • 1
    There's no point in having a non-abstract, unencapsulated "factory". It's just adding meaningless jargon and [OOP-ifying something which is really a function](http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html). – spraff Jan 20 '12 at 14:37
  • 2
    @Lightness: I don't think there's consistent use of the term "factory". Often it's used as you did, to mean any function or object that creates objects. Sometimes it's used specifically to mean the Factory Method Pattern or the Abstract Factory Pattern (I think both have a factory interface with concrete implementation(s), although I don't have the GoF book). The trouble with applying the "anything that creates things" definition to return-by-value is that it makes `std::sin` a factory - it takes a `double` and returns a newly-created `double`. So it's kind of meaningless. – Steve Jessop Jan 20 '12 at 14:39
  • "any function or object that creates objects" is far *far* too broad a definition to be meaningful. It includes `rand()` for god's sake! Language shouldn't be more complicated than the concepts it describes, adding redundant words in a sentence is like adding redundant lines in a drawing: messy and distracting. – spraff Jan 20 '12 at 14:41
  • @spraff: indeed, I was just editing my comment to expand on that. – Steve Jessop Jan 20 '12 at 14:43
  • But in C++, if I had a function that returned a `unique_ptr` (or for that matter a `Foo*`) pointing to a newly-allocated object, then I might refer to that as a "factory" even if it's documented that the object always is exactly `Foo`, not a derived class. The reason is that in C++ you care a lot about dynamically-allocated objects, so a function that heap-allocates something feels like an adventure. Maybe it would be better to use a name that the OOP people didn't get to first, though. – Steve Jessop Jan 20 '12 at 14:50
3

The C-Structs can still have member functions. Problems will, however, arise if you start using virtual functions as this necessitates a virtual table somewhere in the struct's memory. Normal member functions (such as a constructor) don't actually add any size to the struct. You can then pass the struct to the DLL with no problems.

Goz
  • 61,365
  • 24
  • 124
  • 204
  • @dangerousdave: If it didn't work then I have a lot of code that would break. Can you give more information on your "experiments"? Do you get a different "sizeof" result for a struct with no constructor and one with a constructor? – Goz Jan 20 '12 at 15:23
  • 4
    In C++11, that's fine since it remains a *standard-layout* class (which can have constructors and other non-virtual member functions). In C++03 it's probably fine in practice, but there's no guarantee that adding a constructor will maintain the layout. – Mike Seymour Jan 20 '12 at 16:26
  • @Mike Seymour: Fair enough, never heard (or furthermore experienced) that before. Can you tell me the particular part of the spec that refers to that issue? – Goz Jan 20 '12 at 19:07
  • C++11, 9/6 defines *standard-layout* classes. I don't have my copy of C++03 to hand, but from memory its "Classes" chapter, probably chapter 9, doesn't define a concept of *standard-layout*, only *POD*; and a class with a constructor is not POD. – Mike Seymour Jan 21 '12 at 10:47
  • @Mike Seymour: Cheers. I'm shocked on that one, tbh, I had no idea. I guess I'm lucky that no compiler I've ever come across goes weird on such things :D – Goz Jan 21 '12 at 12:54
1

I would use a constructor class:

struct Foo { ... };
class MakeFoo
{
   Foo x;
public:
   MakeFoo(<Required-Members>)
   {
     <Initalize Required Members in x>
     <Initalize Members with default values in x>
   }

   MakeFoo& optionalMember1(T v)
   {
      x.optionalMember1 = v;
   }

   // .. for the rest option members;

   operator Foo() const 
   {
     return x;
   }
};

This allows to arbitrary set members of the struct in expression:

processFoo(MakeFoo(1,2,3).optionalMember3(5));
Begemoth
  • 1,389
  • 9
  • 14
  • A valid solution, but it's a bit excessive if you ask me. What's wrong with having multiple statements to build an object? Chaining doesn't save effort at the point of use, and it just adds redundant code elsewhere. – spraff Jan 20 '12 at 14:35
  • 1
    @spraff, Nothing wrong but I personally prefer exprression-oriented interface, no need for variables that hold intmediate values. – Begemoth Jan 20 '12 at 14:41
1

I have an easy idea, here is how:

Make the structure, just like you normally would, and create a simple function that initializes it:

struct Foo{...};

void Default(Foo &obj) {
    // ... do the initialization here
}

If you have multiple structures, you are allowed in C++ to overload the function, so you can have many functions called 'default', each initializing its own type, for example:

struct Foo { //... };
struct Bar { //... };

void Default(Foo &obj) {...}
void Default(Bar &obj) {...}

The C++ compiler will know when to call the first or the second overload based on the parameter. The & makes obj a reference to whatever parameter you give it, so any changes made to obj will be reflected to the variable you put as parameter.

Edit: I also have an idea for how to specify some parameters, you can do it by using default parameters. This is how it works:

For example you the following function; you can specify default values for parameters like this:

void Default (Foo &obj, int number_of_something = 0, int some_other_param = 10)
{ ... }
Tibi
  • 4,015
  • 8
  • 40
  • 64
  • I use this approach sometimes. You can even add additional parameters and use overloads to create a variety of constructors. – edA-qa mort-ora-y Jan 20 '12 at 14:53
  • How exactly are default parameters "new"? They've _always_ been valid in standard C++, and I think for quite some time even before that. – Lightness Races in Orbit Jan 20 '12 at 14:55
  • You're right, I learned recently about them, and I made the assumption that they were new. I did some research, and now I know :D – Tibi Jan 20 '12 at 14:58