Returning boost::shared_ptr

Started by
17 comments, last by Zahlman 17 years ago
Hi, I've been persuaded to use boost::shared_ptrs instead of standard pointers but have wasted alot of time with these time-saving helpers. My newest problem is this: With standard pointers, a function may look like this:


CMyClass* myClass;

CMyClass* ReturnClassOrNull( bool b )
{
   if ( b )
      return myClass;
   else
      return NULL;
}

This ability to return a NULL was very useful, since it can be easily checked for validity. However this version produces an error, since a boost::shared_ptr is actually an object, not a pointer:


boost::shared_ptr<CMyClass> myClass;

boost::shared_ptr<CMyClass> ReturnClassOrNull( bool b )
{
   if ( b )
      return myClass;
   else
      return NULL;
}

Is there any way a shared_ptr can return the equivalent of a NULL? Thanks Si
Advertisement
Yes. Example:(I prefer to typedef smart pointers so I don't write the same things over and over):

typedef boost::shared_ptr<CMyClass> PMyClass;PMyClass myClass;PMyClass ReturnClassOrNull(bool b){  if (b)    return myClass;  else    return PMyClass();}


I see. thanks.
I use a shared_ptr instanciated with the default constructor instead of NULL.

shared_ptrs can be tested for truth (eg if(mysptr) ) in the same way as pointers.

I believe this is the intended use. Also, you'll get an assertion failure if you attempt to dereference a default sptr.

edit: oops, didn't read mikeman's response properly.
[size="1"]
I find it ugly to write "return boost::shared_ptr<T>()" so I usually use a helper class:

const struct null_ptr_t{   template <typename T>   operator boost::shared_ptr<T>() const   {      return boost::shared_ptr<T>();   }} null_ptr;boost::shared_ptr<int> foo(){   return null_ptr;}boost::shared_ptr<SomeOtherType> bar(){   return null_ptr;}int main(){   boost::shared_ptr<int> a = foo();   boost::shared_ptr<SomeOtherType> b = bar();}
Arguing on the internet is like running in the Special Olympics: Even if you win, you're still retarded.[How To Ask Questions|STL Programmer's Guide|Bjarne FAQ|C++ FAQ Lite|C++ Reference|MSDN]
Quote:Original post by sipickles
I've been persuaded to use boost::shared_ptrs instead of standard pointers but have wasted alot of time with these time-saving helpers.
Since I think I was the one who suggested managing your resources via shared_ptr, I should probably comment on this :)

If you're new to smart pointers and/or the Boost libraries it might take a little time to get used to the semantics and the finer points of usage, but I'm sure it will pay off in the long run.

Problems with passing 'standard' raw pointers around include:

1. You have to remember to delete them.
2. You can delete them at any time, even if 'live' pointers to the same object still exist elsewhere in the program.
3. There's no way to check a non-null pointer for validity - you just have to dereference it and hope for the best.

I think the 'wrapper' class you had in place previously was intended to address some of these problems, but shared_ptr is tried and tested and addresses all of the problems listed above. (Just to be thorough, you can still get yourself in trouble through careless use of the get() function, but this more or less falls into the category of 'deliberate misuse'.)

Also, as noted above shared_ptr shares similar semantics with raw pointers in several important respects. You can check them for validity via Boolean expressions, and you can have a 'null' shared pointer object that represents an 'empty' or unassigned pointer.

shared_ptr offers additional useful functionality as well; for example, at any time you can query a pointer to find out how many times the held object is referenced throughout the program (provided you've used smart pointers consistently).

Boost also has a few other useful smart pointer classes that 'fill in the gaps', so to speak. weak_ptr can be used to break cyclic dependencies, and scoped_ptr can be used where sole ownership is desired. (However, AFAIK scoped_ptr cannot easily be used with resources such as the DirectX effects under discussion in your previous thread, as it does not offer support for custom deleters).

So yes, when adopting a new tool there will probably be a little 'bump' and a little extra time spent implementing features that may at first seem like they were 'easier' the old way. Nevertheless, management of dynamically allocated resources via smart pointers represents the current best practice in C++ (or so I gather - I hope the resident gurus will correct me if I'm wrong in this respect), and should be preferred over the use of raw pointers where possible. In the end, it should save you more time (in terms of easier maintenance and fewer bugs) than it costs.
Thanks to everyone for the help, particularly jyk for the eloquent, encouraging and ultimately persuasive posting. :)

-----------------------------------------------

All my reworking of my original problem is still leading me to the same problem. I am trying to get my resource manager template to create a new resource and return a boost::shared_ptr to it.

Unfortunately, I come unstuck when I try to create the resource in the template resourceManager:

// Type: boost::shared_ptr< CEffect >// std::map< std::string, Type > m_list;		// Create the resource		Type resource( new Type( name, path ) );		// Add the new resource to the manager and return a smart_pointer to it.		m_list[name] = resource;		return m_list[name];


The create resource line is meant to create a new CEffect, but the type is boost::shared_ptr< CEffect >, so I get an error (wrong constructor).

How do I actually allocate the object in this template? Its all abstracted one level....

Thanks


[Edited by - sipickles on March 28, 2007 2:42:30 PM]
Your problem is here:
Type resource( new Type( name, path ) );

Since Type is shared_ptr, you're trying to construct a shared_ptr with the CEffect's constructor and then copy that into a second shared_ptr.

Solution:
Type resource = new Type::element_type(name, path);

shared_ptr::element_type resolves to the type pointed to by shared_ptr, which in this case is CEffect.

(Please note that this is off the top of my head so the precise syntax for getting at element_type may not be right; I don't do it very often.)

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Ah, thanks

I think I solved it another way. I create the template using my object type, then in the resource manager use boost::shared_ptr< Type >. The offending line then becomes

// Create the resourceboost::shared_ptr<Type> resource( new Type( name, path ) );


Your way is neater tho!
So it continues... now the problem is with shared_ptr::reset()

	bool Remove( std::string name )	{		// Iterate the list looking for the specified resource.		std::map< std::string, boost::shared_ptr< Type > >::iterator iter;// = m_list.begin();		for ( iter = m_list.begin(); iter != m_list.end(); ++iter )		{			if( name.compare( iter->first ) == 0 )			{				g_log->SIM( "Removing reference to %s (%d references)\n", const_cast<char*>(iter->second.get()->GetName().c_str()), iter->second.use_count() );				iter->second.reset();				if ( iter->second.use_count() == 0 )				{					g_log->SIM( "Resource %s now deleted, removing from ResourceManager: \n", const_cast<char*>(name.c_str()) );					m_list.erase( name );					g_log->SIM( "ResourceManager has %d resources left\n", m_list.size() );				}				return true; 			}		}		g_log->SIM( "ERROR - Resource %s not found during ResourceManager::Remove()\n", const_cast<char*>(name.c_str()) );		return false;	}


When the above function is called, there are 3 refs to the CEffect in question. Calling:

iter->second.reset()

nulls the .second part of the std::map. Fine. My problem is, how can I check if the object is still in use elsewhere (has it been automatically destructed?) coz if it has, I need to remove it from my std::map.

Thanks again

This topic is closed to new replies.

Advertisement