Sign in to follow this  
Erzengeldeslichtes

Unknown number of starting variables?

Recommended Posts

I have a resizable array that's been specifically designed for my types. Whenever I add something to the array I usually create it as I'm adding it, ie Array.AddElement(new Object(params));. My current difficulty is that sometimes I take the array as a parameter, and just as I create as I add, I want to be able to do that in parameter form. For example, say Func takes a single parameter of the array, what I want to be able to do is: Func(Array(new Object(1), new Object(2), new Object(3))); or Func(Array({new Object(1), new Object(2), new Object(3)}));. There are two possibilites I can see. 1) Variable Argument Lists, where I have an Array Constructor of Array::Array(T* Object, ...);, and then the rest are found using the varg setup. Concerns I have with this are: does it type check? If not, couldn't someone do Array(new Object(1), string("hello"), 5, 1.0f);? I suppose I could throw an error and let the programmer that do h(er/is) job and fix it if I can't dynamic_cast to T* or something. 2) Static Array, where I have an Array Constructor of Array::Array(unsigned int NumObjects, T** Objects). Concerns I have with this are: It isn't automatically numbered, so someone could specify NumObjects = 0xFFFFFFFFF and then only supply 2 objects. (which again would cause problems down the line for the programmer using it to do h(er/is) job and fix the incorrect passing) Is there a happy medium I'm not seeing? I am using Microsoft Visual C++ .Net 2005 Express Edition Beta 1--using unmanaged C++ ("native code"?). Also I would like to note for those of you who might be concerned by my create-as-I-add, the array holds smart pointers. This requires dynamic creation, but deletes when everything (including this array) is done. So the create-as-I-add doesn't leak memory. I don't know if it's much less efficient since I end up using the heap more than I used to, but I find it much easier since I can cross scope all I want (without copying) and still get everything deleted. Of course, since I'm probably the only one to use this, I can just use vargs and know that I shouldn't put in strings, etc...

Share this post


Link to post
Share on other sites
1) No, varargs do not typecheck. And passing an object (as opposed to a pointer to an object) like std::string has undefined behaviour, which is standardese for "don't do this".

2)


class Array
{
std::vector<Object*> vec;
public:
Array() {}

Array(Object* obj)
: vec(1)
{
vec[0] = obj;
};

Array& operator()(Object* obj)
{
vec.push_back(obj);
return *this;
}
};

Func( Array(new Object(1))(new Object(2))(new Object(3)) );
// or
Func( Array()(new Object(1))(new Object(2))(new Object(3)) );




You can consider using other operators, too, like << or %

operator, is a possibility, but to prevent the compiler from thinking you are passing multiple arguments to Func(), I believe an extra set of parentheses will be necessary, and it may lead to confusion anyway.

If you want to make it accept any type (and do whatever type-checking may be needed yourself), make the operator into a templated operator.

All the magic really is in the Array&...return *this which allows chaining.


template<class T>
Array& operator()(T &obj)
{
Object* ptr = CONVERT_TO_OBJECT_POINTER(obj);
vec.push_back(ptr);
return *this;
}

Share this post


Link to post
Share on other sites
variable arguments are a bad idea, no type checking, you'll have to do this at run-time, something like printf does (apparently some compilers printf actually does do type checking at compile time pretty cool).

Also variable arguments don't gel well with proper user-defined types.

I would recommend you use the standard library vector instead.

Share this post


Link to post
Share on other sites
That's a good idea Fruny. Better than using vargs etc. Now, operator(), operator&, operator +, operator +=? Decisions, Decisions (I think I can scratch the last two, or at least the second to last, it would be a bit confusing since operator+ usually returns a copy instead of referance. Then again so does operator&...)
Quote:
Original post by Fruny
operator, is a possibility

there's an operator comma? I didn't know that.

Quote:
Original post by snk_kid
I would recommend you use the standard library vector instead.


And would the std::vector allow me to do std::vector<object*>(new object(1), new object(2), new object(3), {so on and so forth}, new object(n));? If so, there would have to be a way in C++ if STL can.
I could do as Fruny showed in the source snippet, use std::vector within the array that is specialized for my types (I do believe I mentioned that), but the wrapper would still need the ability to be created fully in line.

[Edited by - Erzengeldeslichtes on September 19, 2004 6:38:23 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Erzengeldeslichtes
there's an operator comma? I didn't know that.


Yes there is. It is generally only seen in for loops: for(int i,j; i<100 && j<100; i+=3, j+=i)

However, be careful: overloading it, just like overloading && and || makes it lose its special semantics: non-overloaded || and && do short-circuit evaluation; non overloaded comma forces the left side to be evaluated before the right side.

So, in the above expression, i+=3 is guaranteed to be evaluated before j+=i.

Once they are overloaded, &&, || and the comma operator behave like any other function call, and the order of evaluation of the parameters (left & right sides of the operator) are undefined: j+=i could use the original or incremented value of i

Share this post


Link to post
Share on other sites
Quote:
Original post by Erzengeldeslichtes
And would the std::vector allow me to do std::vector<object*>(new object(1), new object(2), new object(3), {so on and so forth}, new object(n));? If so, there would have to be a way in C++ if STL can.


Yes not directly but there is a way, read on [smile].

Quote:
Original post by Erzengeldeslichtes
I could do as Fruny showed in the source snippet, use std::vector within the array that is specialized for my types (I do believe I mentioned that), but the wrapper would still need the ability to be created fully in line.


If i understood you correctly, you don't need to specialize vector for your types or a pointer to your types, vector will most definitely generate a vector of your types equal to or even better than your hand-written resizable array.

By default storing pointers in STL containers can be a little inefficent but the standard library containers are parameterized by allocator type so you can write/use a custom allocator instead of using the default standard one, boost has a pooled allocator type for STL containers this would be good for storing pointers & smart pointers because they are small objects and they are always the same size.

You also mentioned something about storing smart pointers, well with also this in mind, here is a vector of boost's smart pointers using a boost's pooled allocator and achieving what your trying to do:


#include <boost\pool\pool_alloc.hpp>
#include <boost\smart_ptr.hpp>
#include <vector>

struct foo {
virtual ~foo() = 0;
};

foo::~foo() {}

struct bar : foo {
~bar() {}
};

struct bar2 : foo {
~bar2() {}
};

struct bar3 : foo {
~bar3() {}
};

typedef boost::shared_ptr< foo > foo_ptr;
typedef boost::pool_allocator< foo_ptr > foo_ptr_allocator;
typedef std::vector< foo_ptr, foo_ptr_allocator > vec_of_foo;

int main() {

foo_ptr my_foos[] = { foo_ptr(new bar), foo_ptr(new bar2), foo_ptr(new bar3) };

vec_of_foo foos(my_foos, my_foos + 3);

return 0;
}


edit: going foobar nutz

[Edited by - snk_kid on September 19, 2004 7:04:08 PM]

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
You won't be able to use the product of an overloaded operator, with std::vetor [atleast not without an intermediate function that collects] because you can't change the type it accepts as a constructor argument, but you could with you own hand written container class. The best plan would be to endure a slight inconvenience and persist with snk_kid's array idea.

snk_kid: Yep, some printf's do check the arguments. My HiTech Lite C cross-compiler [for the 80c552's] check whether a 16-bit int or 8-bit char is passed, though these are the only integral types [withstanding signed variations].

Share this post


Link to post
Share on other sites
Another simple idea, using operator, as fruny suggested.

#include <iostream>
#include <vector>

typedef std::vector<int> int_vec;

void f(int_vec& v) {
int_vec::iterator end = v.end();
for(int_vec::iterator i = v.begin(); i != end; ++i) {
std::cout<<*i<<std::endl;
}
}

template<typename T>
std::vector<T>& operator,(std::vector<T>& v, T const& t) {
v.push_back(t);
return v;
}

int main() {
int_vec v;
f((v, 10, 11, 12, 14));
}

Share this post


Link to post
Share on other sites
Quote:
Original post by snk_kid

foo_ptr my_foos[] = { foo_ptr(new bar), foo_ptr(new bar2), foo_ptr(new bar3) };

vec_of_foo foos(my_foos, my_foos + 3);

return 0;
}


That's the same thing as option 2, except not in a single line. With option 2 I could do Array(3, {new bar, new bar2, new bar3}); and have exactly the same result except with my array.
Again, the problem is I have to rely on you to give me the correct number. What if I did vec_of_foo foos(my_foos, my_foos+12); without any other changes to your code? Does std::vector have some way of bounds checking a dumb-array?

Of course the downside with the overloaded operators is that it then only adds one at a time, requiring the most number of resizes, wheras with varg I'd be able to get the number of elements (I think), size it to that, and add them. Same deal with static array, I size it to the size you give me and then copy that many from your static array.

If I were to switch to std::vector I'd still have to either create a wrapper or derive a new vector because I have functions in the array that work on the members of the array (as I said, it's specialized for my types)

Share this post


Link to post
Share on other sites
Quote:
Original post by Anonymous Poster
Switch to STL.


Statements such as "switch to STL" are exceedingly annoying. Give me an intelligent response, with intelligent reasoning. You gave no reasoning. "You can only have 2 of elegance, safety, and efficiency." If this is your reasoning, you forgot to specify which 2 STL is (and even if you had it would have been insufficient).
If STL is safety and efficiency, perhaps I prefer elegance and safety?
Please, give me a good reason why I should spend the next week revamping my game to use STL rather than spending 5 minutes implementing a new constructor.

Share this post


Link to post
Share on other sites
With varargs, you'd still have to pass the number of parameters to know how many they passed. There is no good solution to this really. Using a function that returns a reference to 'this' and chaining calls is probably the best solution.

Well, you could do function overloads like make 15 versions of MakeArray that each take a different number of arguments (for 1-15), so it would only accept the proper number of the proper type.

Edit: Fruny said it wouldn't work so I deleted the guesswork code and made the idea really simple =-)

Share this post


Link to post
Share on other sites
Quote:
Original post by Erzengeldeslichtes
That's the same thing as option 2, except not in a single line. With option 2 I could do Array(3, {new bar, new bar2, new bar3}); and have exactly the same result except with my array.


Why do you even bother considering 'options' which are illegal? There is no such thing as an array literal.

Quote:
Of course the downside with the overloaded operators is that it then only adds one at a time, requiring the most number of resizes, wheras with varg I'd be able to get the number of elements (I think), size it to that, and add them.


No. Varargs require you to, somehow, tell the compiler when the end of the argument list is reached. printf does it simply by counting the placeholders. Another option is to end the list with a null pointer (as the argv array is).

If you're worried about performance, add an initial parameter that calls std::vector::reserve(). All that an incorrect parameter costs you is some time - you won't have any correctness problem.

Quote:
Same deal with static array, I size it to the size you give me and then copy that many from your static array.


Except you still have to find that size, somehow. There are template tricks you can pull to make sure a genuine array is given as parameter (yes, in two steps) and find the number of its elements.

[quote[If I were to switch to std::vector I'd still have to either create a wrapper or derive a new vector because I have functions in the array that work on the members of the array (as I said, it's specialized for my types)[/quote]

What's wrong with creating wrappers?
STL containers are not designed to be inherited from.

Quote:
Original post by Anonymous Poster

Dude, you can only have 2 of elegence, safety and efficiency. Switch to STL


You can have all three, though making code elegant from the user's point of view often requires much work from the implementor's point of view.


Extrarius - You've done function overloads, not template specialisations. I don't think that'll work as you want it to.

Share this post


Link to post
Share on other sites
Quote:
Original post by Fruny

Why do you even bother considering 'options' which are illegal? There is no such thing as an array literal.

Oh, is it illegal? My bad then. It would then need to be done in 2 lines:
Object* Arr[] = {new bar, new bar2, new bar3};
Array(3, Arr);

I had thought of the overloading, but I figured there had to be a better way then that.

However, I have used vargs before and I could have sworn that I was able to get the number of variables without providing it, though it's been a while so maybe I've just forgotten.
Edit: After testing out the vargs, you're right, I can't figure out the number without using either putting a 0 at the end or providing the number.

[Edited by - Erzengeldeslichtes on September 19, 2004 9:42:10 PM]

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Quote:
Original post by Erzengeldeslichtes
Quote:
Original post by Anonymous Poster
Switch to STL.


Statements such as "switch to STL" are exceedingly annoying. Give me an intelligent response, with intelligent reasoning. You gave no reasoning. "You can only have 2 of elegance, safety, and efficiency." If this is your reasoning, you forgot to specify which 2 STL is (and even if you had it would have been insufficient).
If STL is safety and efficiency, perhaps I prefer elegance and safety?
Please, give me a good reason why I should spend the next week revamping my game to use STL rather than spending 5 minutes implementing a new constructor.

If you think it'll take a week then don't, though the syntax you wan't may not be possible. In general [read: future], switch to STL. It's all done, baby.

Quote:
quote by funy
You can have all three blah blah blah

Yep, but have you downloaded Alexandrescu's flex_string? It's long, real long. So long you could cut it in half, half again and half once more and it's still be long. Real long.

Share this post


Link to post
Share on other sites
Quote:
Original post by Anonymous Poster
Yep, but have you downloaded Alexandrescu's flex_string? It's long, real long. So long you could cut it in half, half again and half once more and it's still be long. Real long.


Doesn't look too bad to me.

This is a policy-based design, with four different storage policies (SimpleStringStorage, AllocatorStringStorage VectorStringStorage), two optimization storage adaptors (SmallStringOpt and CowString), and the flex_string itself which merely implements the requirements for a standard C++ string class.

I don't understand what you feel is wrong with that.

Share this post


Link to post
Share on other sites
Quote:
Original post by Anonymous Poster
It's long. More precisely, the flexibility of this design increases the amount of code.


That doesn't make it any less elegant in my eyes.
Elegant != über-clever hack that fits on one line.

Each of the Storage policy class stands on its own, if you only had a single of those, you wouldn't really have much more code than the standard std::string class (which does rely on an Allocator class).

The extra code really is a language characteristic : C++ does require a certain amount of scaffolding around the "core" of your code.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Actually, whilst I have your attention Fruny, inside the definition of template meh { ... }, is it standard to be able to refer type meh as just meh? Compilers do it, but is it right?

Share this post


Link to post
Share on other sites
Depends on what you mean by 'meh'.
If it is the template parameter then, yes.
If it is the class name, then I believe it is generally OK.

You sometimes have to explicitely specify meh<T> even within the class (e.g when referring to a templated base class), though I don't know what the exact rules are.

Share this post


Link to post
Share on other sites
Quote:
Original post by Anonymous Poster
Tisn't m'love, 'cept the OP wants both in what seems like a couple o lines...

More accurately a single line. I just like using constructors when I can rather than having to occupy several lines setting things. Why should I have to do .SetGravity, .SetMass, .SetPosition, .SetRotation, when I can just do .Set(Gravity, Mass, Position, Rotation)? If at a later point I just want to change gravity, then sure, .SetGravity, but at initialization I usually have the information ready. Similarly having to do:

Array<Object, false, false, true> Arr;
Arr.Add(This);
Arr.Add(That);
Arr.Add(Other);
Arr.Add(Foo);
Arr.Add(Bar);

seems silly.

Array<Object, false, false, true> Arr(This)(That)(Other)(Foo)(Bar);

Seems less silly to me. Both have that problem I mentioned earlier of being the most number of resizes, only way around that is:

Pointer<Object, false, false> PtrArr[5];
PtrArr[0]=This;
PtrArr[1]=That;
PtrArr[2]=Other;
PtrArr[3]=Foo;
PtrArr[4]=Bar;
Arr.AddArray(5, PtrArr);

But again this feels silly that I have to create an array and set them explicitly just to add it to another array.

Share this post


Link to post
Share on other sites
Quote:

Why should I have to do .SetGravity, .SetMass, .SetPosition, .SetRotation, when I can just do .Set(Gravity, Mass, Position, Rotation)?


Because that way you don't have to document which of the parameters is Gravity, which is Mass, which is Position, which is Rotation, and have no fear of swapping them?

An alternative would be to use a language which supports keyword parameters.

Furthermore,


Array<Object, false, false, true> Arr;
Arr.Add(This);
Arr.Add(That);
Arr.Add(Other);
Arr.Add(Foo);
Arr.Add(Bar);

is more readable than

Array<Object, false, false, true> Arr(This)(That)(Other)(Foo)(Bar);


You can chain member functions, not just operators: Arr.Add(This).Add(That).Add(Other).Add(Foo).Add(Bar); may be a good idea.

Also, but that's a personal preference, I see not point in having Get or Set in the name of the member function:


class Foo
{
int x, y;
public:
int X() const { return x; }
int Y() const { return y; }

Foo& X(int value) { x = value; return *this; }
Foo& Y(int value) { y = value; return *this; }
};


Also, I feel that people tend to have too many Get/Set methods.

Quote:
Both have that problem I mentioned earlier of being the most number of resizes, only way around that is


No. std::vector has a reserve() member function. Use it.

Share this post


Link to post
Share on other sites
Quote:

Why should I have to do .SetGravity, .SetMass, .SetPosition, .SetRotation, when I can just do .Set(Gravity, Mass, Position, Rotation)?


Because that way you don't have to document which of the parameters is Gravity, which is Mass, which is Position, which is Rotation, and have no fear of swapping them?

An alternative would be to use a language which supports keyword parameters.

Furthermore,

Quote:

Array<Object, false, false, true> Arr;
Arr.Add(This);
Arr.Add(That);
Arr.Add(Other);
Arr.Add(Foo);
Arr.Add(Bar);

is more readable than
Quote:

Array<Object, false, false, true> Arr(This)(That)(Other)(Foo)(Bar);


You can chain member functions, not just operators: Arr.Add(This).Add(That).Add(Other).Add(Foo).Add(Bar); may be a good idea.

Also, but that's a personal preference, I see not point in having Get or Set in the name of the member function:


class Foo
{
int x, y;
public:
int X() const { return x; }
int Y() const { return y; }

Foo& X(int value) { x = value; return *this; }
Foo& Y(int value) { y = value; return *this; }
};


Also, I feel that people tend to have too many Get/Set methods.

Quote:
Both have that problem I mentioned earlier of being the most number of resizes, only way around that is


No. std::vector has a reserve() member function. Use it.

Share this post


Link to post
Share on other sites
Quote:
Original post by Fruny
You can chain member functions, not just operators: Arr.Add(This).Add(That).Add(Other).Add(Foo).Add(Bar); may be a good idea.

Yes, I know. The thing is that Add takes a dumb pointer and returns a smart pointer to what's held in that dumb pointer. I need that functionality, so I have to have a seperate function to return for the chain, and I might as well just use operator().
Quote:
Also, I feel that people tend to have too many Get/Set methods.

That was an example, from one of my complaints about NovodeX was that I have to do

Nx{something}Desc Blah;
Blah.gravity = Gravity;
Blah.mass = Mass;
Blah.position = static_cast<float*>Pos;
Blah.orientation = D3DVec3ToNxQuat(Rotation);

So, rather than complain, I built a helper structure that had a constructor and a function for getting the description. I don't use Get/Set unless it's necessary, for example my physics objects have Get/Set because they do additional calculations with the data they recieve--mostly converting the D3DXVECTOR3 that I'm giving into an NxVec3 and relaying the get/set to NovodeX, and sometimes updating peices of information I need but NovodeX doesn't allow me to get from their objects.
You're right though, it's not necessary to name them Get/Set when they are used.
Quote:
Both have that problem I mentioned earlier of being the most number of resizes, only way around that is


No. std::vector has a reserve() member function. Use it.[/quote]
Yes, I could make a reserve function as well. When I created the array I had decided that the usual use of the array doesn't require a reserve function and the assosciated member variable. I might need to reevaluate that decision.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this