Unknown number of starting variables?

Started by
26 comments, last by Erzengeldeslichtes 19 years, 6 months ago
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...
----Erzengel des Lichtes光の大天使Archangel of LightEverything has a use. You must know that use, and when to properly use the effects.♀≈♂?
Advertisement
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)) );// orFunc( 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;  }
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
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.
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]
----Erzengel des Lichtes光の大天使Archangel of LightEverything has a use. You must know that use, and when to properly use the effects.♀≈♂?
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
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
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]
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].
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));}

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

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)
----Erzengel des Lichtes光の大天使Archangel of LightEverything has a use. You must know that use, and when to properly use the effects.♀≈♂?
Dude, you can only have 2 of elegence, safety and efficiency. Switch to STL.

This topic is closed to new replies.

Advertisement