Thread: intialize array allocated by new

  1. #16
    The larch
    Join Date
    May 2006
    Posts
    3,573
    How about the following? I'm particularly interested in the const references.

    Code:
    #include <iterator>
    #include <iostream>
    
    using namespace std;
    
    namespace detail
    {
    template <class, class, unsigned>
    class block_n;
    
    template <class Block, class T, unsigned N>
    T* allocate(const block_n<Block, T, N>& block);
    
    template <class Block, class T, unsigned N>
    class block_n
    {
        const Block& left;
        const T& value;
    public:
        block_n(const Block& left, const T& value): left(left), value(value) {}
        block_n<block_n<Block, T, N>, T, N + 1u> operator, (const T& t) const
        {
            return block_n<block_n<Block, T, N>, T, N + 1u>(*this, t);
        }
        T* assign(T* p) const
        {
            p = left.assign(p);
            *p = value;
            return ++p;
        }
        operator T* () const
        {
            return allocate(*this);
        }
    };
    
    template <class T>
    class block_n<void, T, 1u>
    {
        const T& value;
    public:
        block_n(const T& t): value(t) {}
        block_n<block_n<void, T, 1u>, T, 2u> operator, (const T& t) const
        {
            return block_n<block_n<void, T, 1u>, T, 2u>(*this, t);
        }
        T* assign(T* p) const { *p = value; return ++p; }
        operator T* () const
        {
            return allocate(*this);
        }
    };
    
    template <class Block, class T, unsigned N>
    T* allocate(const block_n<Block, T, N>& block)
    {
        T* p = new T[N];
        try {
            block.assign(p);
        } catch (...) {
            delete [] p;
            throw;
        }
        return p;
    }
    } //namespace detail
    
    template <class T>
    class block
    {
    public:
        detail::block_n<void, T, 1u> operator, (const T& t)
        {
            return detail::block_n<void, T, 1u>(t);
        }
    };
    
    int main( void )
    {
        int*  array = ( block< int >( ), 1, 2, 3 );
        copy( array, array + 3, ostream_iterator< int, char >( cout, "\n" ) );
        return 0;
    }
    P.S For all practical purposes this syntax is meaningless. The size of the array is deduced from the number of arguments only. Other code has no means of finding out the size of the dynamically allocated array. (Compare with how you can find out the size of the stack array even if you let the array's size be deduced from the initalization arguments.)

    Best thing to do is to hope that the hard-coded 3 in the copy call is correct and no-one removes any arguments from the initialization...
    Last edited by anon; 06-09-2009 at 02:40 PM.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  2. #17
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    >> Sebastiani points out the exception safety missing from that example, but here's another way you could add it:

    In this particular case it was actually already exception safe (without the try block), but anyway, in general I think your approach can be quite useful in many different contexts. In fact, after you posted that I realized that something like that might even be useful for implementing "function within a function" semantics.

    >> With an almost negligible change in the interface you can bypass additional copies mandated by the assignment and possibly avoid allocation.

    I wouldn't design the whole thing around avoiding copies, necessarily, but it would probably be a good idea to provide an extended interface to allow it.

    >> How about the following? I'm particularly interested in the const references.

    That's pretty cool, actually. I didn't even consider a compile-time solution. The only drawback to something like this, though, would be really large array initializations. The effect on compilation time can be noticeable:

    Code:
    int main( void )
    {
    	char*
    		text = 
    	( 
    		block< char >( ), 
    		'T', 'e', 's', 't', 'i', 'n', 'g', '\n',
    		'T', 'e', 's', 't', 'i', 'n', 'g', '\n',
    		'T', 'e', 's', 't', 'i', 'n', 'g', '\n',
    		'T', 'e', 's', 't', 'i', 'n', 'g', '\n',
    		'T', 'e', 's', 't', 'i', 'n', 'g', '\n',
    		'T', 'e', 's', 't', 'i', 'n', 'g', '\n',
    		'T', 'e', 's', 't', 'i', 'n', 'g', '\n',
    		'T', 'e', 's', 't', 'i', 'n', 'g', '\n',
    		'T', 'e', 's', 't', 'i', 'n', 'g', '\n',
    		'T', 'e', 's', 't', 'i', 'n', 'g', '\n',
    		'T', 'e', 's', 't', 'i', 'n', 'g', '\n',
    		'T', 'e', 's', 't', 'i', 'n', 'g', '\n',
    		'T', 'e', 's', 't', 'i', 'n', 'g', '\n',
    		'T', 'e', 's', 't', 'i', 'n', 'g', '\n',
    		'T', 'e', 's', 't', 'i', 'n', 'g', '\n',
    		'T', 'e', 's', 't', 'i', 'n', 'g', '\n',
    		'T', 'e', 's', 't', 'i', 'n', 'g', '\n',
    		'T', 'e', 's', 't', 'i', 'n', 'g', '\n',
    		'T', 'e', 's', 't', 'i', 'n', 'g', '\n',
    		'T', 'e', 's', 't', 'i', 'n', 'g', '\n',
    		'T', 'e', 's', 't', 'i', 'n', 'g', '\n',		
    		'T', 'e', 's', 't', 'i', 'n', 'g', '\n',
    		'T', 'e', 's', 't', 'i', 'n', 'g', '\n',
    		'T', 'e', 's', 't', 'i', 'n', 'g', '\n',
    		0	
    	);
    	cout << text;
    	delete [ ] text;
    	return 0;
    }
    Anyway, it is rather ingenious, and so I've added it to my library of "interesting uses of templates".
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  3. #18
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    Quote Originally Posted by phantomotap View Post
    Still, you did add, after a fashion, a smart pointer that works with arrays. I hadn't thought of using a nested class to bypass the work. That was a good bit of sorcery.
    Well that was about all I intended. You got the point exactly.

    I only suggested I would take the vector approach because it is quicker to implement.


    Sebastiani:
    It wasn't providing even the weak exception guarantee, if I show you where I'm coming from here. Sure the code happened to only be used with ints, but if code is templated then really it should be written such that it at least meets weak exception guarantees regardless of the templated type, IMO. That's what I meant.
    In that light, std::copy can in general cause an exception to be thrown if the copy-construction of an item fails. Sure it wont fail for ints, but a string class say may fail to allocate memory required for the construction for example. It would then have leaked.
    The code I posted on the other hand, has strong exception guarantees in that regardless of the templated type, it not only never leaks memory, but it also either performs it's function completely, OR it throws an exception and leaves the object completely unchanged.
    I just wanted to demonstrate how simple it can be to achieve this.

  4. #19
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Well that was about all I intended. You got the point exactly.
    You just intended to offer a nested shared pointer?! O_o

    Okay. I thought you must have been making a point I wasn't getting.

    It wasn't providing even [...] what I meant.
    Do you mean the basic guarantee? If so, that's true; my code only prevented failure from trashing an existing resource--which I find far more destructive. (Between the two options, I'd much rather "lose" a resource than trash an existing one.)

    In that light, std::copy can in general [...] have leaked.
    That would be an assignment failure which would probably occur, in practice, a little more often. (The existing object would have its resources and would likely require still more resources to construct a temporary object to handle the assignment.)

    The code I posted on [...] the object completely unchanged.
    This is not true. (I'll grant you, because of the interface, you are providing the strong guarantee.) Your code only makes the least modification I suggested. (I'm not saying you didn't see the source and say "Oh, that needs a smart pointer." for yourself.) To succeed or fail utterly (commit or rollback semantics) you still have more work to do. (If you don't see it, stick around after the signature.) In this case, you can get away with a pretty simple fix.

    I just wanted to demonstrate how simple it can be to achieve this.
    It is extremely difficult, unbelievably costly, or even impossible depending on the interface. (You just can't take a bad interface and make it holy. It must be redesigned.)

    Soma

    Code:
    data[size++] = value;
    At this point in execution the protected state (`Type * data;' and `size_t capacity;') may have been mutated. This line can fail as readily as every assignment related to the `std::copy' call. If this assignment fails the operation fails and the object has changed.

    For this silly thing, it isn't that major, but take a moment to consider the implications.

    For this implementation you need only to update the trivial state (the pointer and the integers) after the new value has been assigned to the new array. (For those who don't know, such assignment operations on trivial types can't throw an exception so they can be used without further concern.)

  5. #20
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    Yes I meant assignment not copy-construction.

    It can be made to work if the type "Type" also has the strong exception guarantee for its assignment operator (just put the increment of size in a separate statement after the assignment), but yes you're right that otherwise that last line is problematic.

  6. #21
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    >> For this silly thing, it isn't that major, but take a moment to consider the implications.

    Well, obviously, in the case of a full-featured object one would be concerned with the state of such things as 'size', 'capacity', etc, but this was just a demonstrative example that assumes the user won't be cacheing the object for some later use. In that case, a handful of temporary variables to hold the results and a RAII approach to memory management (such as suggested by iMalc) would suffice.
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 2
    Last Post: 07-11-2008, 07:39 AM
  2. Run-time error with dynamically allocated 3-D array
    By OceanDesigner in forum C Programming
    Replies: 2
    Last Post: 10-21-2005, 02:29 PM
  3. Template Array Class
    By hpy_gilmore8 in forum C++ Programming
    Replies: 15
    Last Post: 04-11-2004, 11:15 PM
  4. passing a 2dim dynamically allocated array to a funct
    By agerealm in forum C++ Programming
    Replies: 3
    Last Post: 03-10-2004, 06:55 PM
  5. Help with an Array
    By omalleys in forum C Programming
    Replies: 1
    Last Post: 07-01-2002, 08:31 AM