Pass Structure

I've been told there's no way to do this, but maybe I was misunderstood...

1
2
3
4
struct BOOKS {
     char lpBook[MINNUM];
     int iBook;
  };


That's a global declaration. Now I declare a function specifically for populating this structure...

void GetData(BOOKS bs[]); // function declaration

1
2
3
4
5
int main()
{
     BOOKS *sb;
     GetData(sb);
}


The above combination is only one of several that I've tried. I get a compiler errro about braces, which is meaningless.

Anyway, in the GetData() function I'm trying to actually populate the structure so...

1
2
3
4
5
6
7
8
void GetData(BOOKS bs[])
{
     bs[] = {
          "one", 0,
          "two", 1,
          "three", 2
        };
}


...but nothing I try works.

In essence, I want to pass a structure to the function so I can fill the structure with data, then pass it back to the pointer, or whatever, and be able to manipulate the data in the calling function.

Is there any syntax available for me to do this?
Last edited on
In main(), BOOKS* sb; just makes a pointer. This is useless unless it points to something.

From the looks of it you want an array of 3 books, so you make it like so:

BOOKS sb[3];

Now if you want to pass this array to another function by pointer, it works like so:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void GetData(BOOKS* bs)
// void GetData(BOOKS bs[])  <--- alternative way, but they're both the same
{
  // note here you can't just use a single assignment operator.  That only
  //  works when first creating structs/arrays (and with structs it's not advisable
  //  -- ctors are a much better solution IMO)

  // anyway this means you have to set them one at a time:
  strcpy(bs[0].lpBook,"one");
  bs[0].iBook = 0;

  strcpy(bs[1].lpBook,"two");
  bs[1].iBook = 1;

 // etc
}


//---------

int main()
{
  BOOKS sb[3];  // our array of 3 BOOKSes

  GetData( sb );  // fills the array

  return 0;
}
No, not really because I don't know the size of the array in main(). IOW, I want the struct declaration to be global, but I don't want to populate it globally. Rather, I want a dedicated function for populating the structure, and I want to be able to go get that data from main(), without first knowing in main() what the size or number of elements/members are.

I hope that makes sense. To say it another way, in the dedicated function I want...

bs[] = {
"one", 0,
"two", 1 };

etc., and I want that data passed back to the calling struct in main(), and then I want to get the size of the struct in main once it's passed back.
Okay then... your function will need 2 outputs: a pointer and the size of the array. This means you'll have to pass the pointer by reference (or by pointer).


This uses a reference to a pointer:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int GetData(BOOKS*& ptrref)
{
  // don't omit the static keyword, here
  static BOOKS bs[] =
  {
   ...  // but again, this kind of initialization for structs is very "ew".  Look up C++ constructors
  };

  // give 'ptrref' a pointer to your 'bs' array
  ptrref = bs;

  // and return the size of the array
  //  sizeof(bs) = size in bytes of the array
  //  sizeof(BOOKS) = size in bytes of one BOOKS structure

  return sizeof(bs) / sizeof(BOOKS);  // therefore this the number of BOOKS
}

//------------------------
int main()
{
  BOOKS* books;
  int numbooks = GetData(books);  // this sets both 'books' and 'numbooks'

  return 0;
}



If you don't like the reference to a pointer deal, you can use a pointer to a pointer. Pretty much exactly the same, but slightly different syntax:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int GetData(BOOKS** ptrptr)  //  double ** is pointer to a pointer
{
  ...

  *ptrptr = bs;
  ...
}

int main()
{
  BOOKS* books;
  int numbooks = GetData(&books);

  return 0;
}



Note that passing back a pointer like this only works if the object(s) pointed to remain "alive" after the function ends. This is why 'bs' must be made static. This also means that all calls to GetData will return the same array of books. From your description, this sounds like this is what you want.
Thanks for the examples. Actually, I don't want the static declaration because I want the structures to pass out of memory when the function ends. I already have the structure declared and defined globally, but since only one .cpp file uses the structure, I'd like it to have only a limited scope from within a function, but since there are 190 elements to the structure, it would be ungainly to define it within the function itself, hence I want a dedicated function for the definition.
Well then you've lost me.

I don't want the static declaration because I want the structures to pass out of memory when the function ends.


If you don't want a static array, then you have 3 options:

1) Create and init the array of structures in the same function they're used in -- ie: don't have a separate GetData function (but you said you don't want to do this)

2) Create the array in the same function they're used it, but pass initialization off to another function by passing a pointer to the array. This is what my first post in the thread does (but this was unsatesfactory)

3) Create the array dynamically and pass a pointer back to the main function. Note that if you do this, the main function will be responsible for cleanup:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int GetData(BOOKS*& books)
{
  int count = /*<however many elements you want>*/;
  books = new BOOKS[count];

  // init all members
    books[0].foo = bar;
    books[1].foo = baz;
    // etc

  return count;
}

int main()
{
  BOOKS* books;
  int count = GetData(books);

    // do stuff with 'books'

  delete[] books; // clean it up when done
  return 0;
}


The only other alternative I can think of is to have the array declared as a constant somewhere, and then copy the data from it. But that's is basically the same thing as having a static array.
First, I appreciate all your help. But it looks like I'm going to have to resort to a class and take advantage of the private operator.

In all the above examples, the one thing that I need, and obviously can't get, with a structure is the size of the structure dynamically. I.e., the way it's set up now, my structure is declared and defined globablly. Thus, when I'm in my function all I have to do to get the size is...

const int MYNUM = ((int) sizeof(bs) / sizeof(bs[0])));

So now I have my number and I can simply declare another structure to that size, or whatever.

So far, there is no code I've seen that allows me to use a dedicated function for the structure definition and get this size dynamically without at least two passes.

Edit: That is to say, I first have to get the size before I can declare a new structure array of that size, so if I have the initial structure populated in the dedicated function, I have to make one pass to get the size, then in the calling function declare a new struct of that size, then call the dedicated function once again in order to get the values into the newly declared structure.
Last edited on
I'm not following you at all. You must know the size of the array -- otherwise how could you have created it?
Okay, let me start over and see if we can get it...

#include "booksort.h"

Within "booksort.h" I have declared thus...

struct BOOKSORT {
char lpBook[MINNUM];
int iBook;
};

Now, in the same file, just under the above, I define the structure...

BOOKSORT bs[] = {
"gen", 0,
"exo", 1,
"lev", 2,

etc... to 190 book-int combinations in all, i.e., taking up all the canonical books, non-canonical books, dead sea scrolls, etc.

190 elements total. The int values increase by one, and these are there so I can sort the books according to canonical order. IOW, I'm simply mapping each book to an int, with "gen" being the first, "exo" the second, and so forth.

Now, this is a global declaration and a global definition in the header file, so all I have to do in my .cpp file is...

BOOKSORT sb[MAXENTRY]; // MAXENTRY is defined as 4096.

However, I also need the size of the bs array in the header files, so I do this...

const int BSTAGS = ((int) sizeof(bs) / sizeof(bs[0])); // from the bs struct in the header file

The reason I need this size is because I have to iterate through all 190 books to see if any of the strings in bs[x].lpBook match a string in sb[i].lpBook. For example...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
for(int i = 0; i < lrVerseNum; i++) // lrVerseNum is the count I got from the listbox
	{
	SendDlgItemMessage(hwnd, iListBox, LB_GETTEXT, i, (LPARAM)(LPSTR)chList[i]);
		
		// find the order of chList[i] by
		// comparing with bs structure, then
		// put that data into the sb structure
		// thus preparing the string for sorting
		for(int x = 0; x < BSTAGS; x++)
		{
		       if( strstr( chList[i], bs[x].lpBook ) != NULL )
		      {
			sb[i].iBook = bs[x].iBook; // copy int to sb for comparison
			strcpy_s(sb[i].lpBook, MINNUM, chList[i]); // put full verse in sb
			break;
		     }
		}
	}


If so, I assign the correspoinding int value to sb[i].iBook so I can sort the structure according to canonical book order, i.e., according to the int values. I started, as you can see, with "gen", 0, then with "Exo", 1, and so on.

When the string matches in the iteration, I assign the appropriate int value so I can sort according to canonical order.

So, the point is, I need the size and the definition right off the bat, and the only way I can see to do this, without resorting to classes, is to declare and define the structure globally.

Does that make sense?
Does that make sense?


.... kind of.

If I'm understanding this right, you have a constant array 'bs' which you are re-sorting into another array 'sb'.... but then you want to destroy 'bs' after 'sb' is sorted?

And you say the problem is that you don't want 'bs' to be global/static... but I'm scratching my head as to why that's a problem. There's really no other practical way to store the information embedded in the code like what you're doing. Other methods might be more dynamic (reading from a file, etc), but if the array is fixed and constant throughout the life of the program, then a static/global const array is a very reasonable option. Is there some reason why static/global is no good?

PS: If you know of a way to do it how you want to with classes, why not use classes? Why are they something you don't want to "resort to"? It sounds like you're saying "I have to drive in this nail, but I don't want to resort to using a hammer.". Although, that said, I don't see how using a class would do much in the way of solving the question you're asking about anyway. So this is kind of a moot point. I just felt like speaking up because people often have irrational resistance to C++ features.


ANYWAY

If you don't want a global array, then you have to do an array copy. I gave all the options for this above -- other solutions would simply be a variation of one of those. If none of those options are what you are looking for, then I'm comfortable saying that what you're looking for is nonexistant.

EDIT:

To explain further.

The "all at once assignment" foo = { stuff, stuff }; can only be done when the array is first created. IE, you cannot pass an array to another function and do this. Nor can you do it on an array that's already been initialized.

Therefore your options for when you do this are limited to two areas:

1) on the heap, as a global (or static) array.
2) on the stack, in a local function.

That's it. Those are your two options. The all at once assignment trick can't be applied any other way.

edit again: doh you posted while I was editing -- I'm replying now. Stay tuned
Last edited on
I'm new to programming, so I was just trying to figure out how to conserve memory. For example, Prata says in his book that it's proper to DECLARE global structures, but then it might be better to define them locally.

Well, with 190 elements, it would be rather ungainly to place them all in the body of a function in a .cpp file. If, however, there was an easy way to have the structure data (i.e., definition) in another function, say at the bottom of the .cpp file, then I could just go get that data from my main function, and then when the calling function was finished, i.e., the dialog box procedure was finished, then the memory that the structure occupied would be freed.

But apparently there's no easy way to do this. At least I haven't figured it out. And in my little program it doesn't make a difference, but what if I had a humongus program? Obviously you can't have globally defined structures taking up memory in a large program, so I have no idea how this is dealt with.
I'm new to programming, so I was just trying to figure out how to conserve memory.


Ironically, the things you were trying to do might result in more memory usage than just having a straight up const global array. const globals might be able to be read directly from the exe off the heap without allocating extra memory for the buffer (though I have no idea if compilers actually do this or not.

Well, with 190 elements, it would be rather ungainly to place them all in the body of a function in a .cpp file


No more ungainly than putting them in a header file. Making them static in a function at least limits their scope.

But apparently there's no easy way to do this


There's plenty of ways. Just not ways that use the all-at-once assignment. Generally you end up making a dynamically allocated buffer and loading it with contents, then freeing that buffer when you're done with it.

Obviously you can't have globally defined structures taking up memory in a large program, so I have no idea how this is dealt with.


Usually clumps of data like this is not stored in the program, but is stored in an external file to be loaded and read dynamically by the program.
Actually, the "ironically" statement makes sense to me. When you consider that there are 190 elements of type char and int, that's an extremely insignificant block of memory, and all the calls I make to from the other functions release the memory they used anyway when the function returns, so I think I might have simply been looking for ways to shoot myself in the foot without knowing it.

Anyway, I appreciate your examples. I learned a bit from them.
Don't know if this helps at all:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct Foo {
   int x;
   char c;
};

template< typename T, size_t N >
T* abegin( T (&a)[ N ] )
{  return a; }

template< typename T, size_t N >
T* aend( T (&a)[ N ] )
{ return a + N; }

std::vector<Foo> GetData() {
    struct Foo foo[] = {
       { 1, 'a' }, { 2, 'b' } //, etc...
    };
  
    return std::vector<Foo>( abegin( foo ), aend( foo ) );
};


Or, closer to your interface:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct Foo {
   int x;
   char c;
};

template< typename T, size_t N >
T* abegin( T (&a)[ N ] )
{  return a; }

template< typename T, size_t N >
T* aend( T (&a)[ N ] )
{ return a + N; }

void GetData( Foo* dest ) {
    struct Foo foo[] = {
       { 1, 'a' }, { 2, 'b' } //, etc...
    };
  
    std::copy( abegin( foo ), aend( foo ), dest );
};
I've never used templates or vector<> so I'll have to experiment with your code. It does seem to eliminate a lot of overhead, especially the second example. Thanks for posting it.
If you are uncomfortable with templates, then I can further simplify at the expense of a small bit of maintainability:

1
2
3
4
5
6
7
8
9
10
11
12
13
struct Foo {
   int x;
   char c;
};

void GetData( Foo* dest ) {
    const size_t NumElems = 2;
    struct Foo foo[NumElems] = {
       { 1, 'a' }, { 2, 'b' } //, etc...
    };
  
    std::copy( foo, foo + NumElems, dest );
};


(All that I was doing before was computing the size of the array without the sizeof(array)/sizeof(elem) trick and without having to keep a separate SIZE variable.
Then the only thing I'm not familiar with there is the std::copy(), but if that does what it looks like it does, it certainly simplifies things.

EDIT: Actually, my program crashes when it runs this. First, when compiling, I get a warning that the copy function may be unsafe, but it does compile, but when I run it, it crashes.
Last edited on
std::copy() is a member-wise version of memcpy. memcpy() is a bit-wise copy, which is why memcpy is really only useful for POD types. std::copy() should work for any type that is Copyable.

The problem is probably BOOKS::lpBook.

If possible, make it a const char* instead of a char array. If that isn't possible, then make it a std::string instead. If that isn't possible, then you'll need to write a copy constructor and assignment operator for BOOKS.
Topic archived. No new replies allowed.