14

I'm using C++ in an embedded linux environment which has GCC version 2.95.

I just can't extract boost::shared_ptr files with bcp, it is just too heavy.

What I'd like would be a simple smart pointer implementation of boost::shared_ptr but without all boost overheads (if it is possible...).

I could come up with my own version reading boost source but I fear missing one or more points, it seems easy to make a faulty smart pointer and I can't afford to have a buggy implementation.

So, does a "simple" implementation or implementation example of boost::shared_ptr (or any reference counting equivalent smart pointer) exists that I could use or that I could take as an inspiration?

PeeHaa
  • 71,436
  • 58
  • 190
  • 262
Nikko
  • 4,182
  • 1
  • 26
  • 44
  • 2
    I'd not worry too much about a buggy smart pointer implementation. G++ 2.95 is a _buggy compiler_. From memory it miscompiles some pretty simple looking code - I'd try to get onto something in the 3.x or 4.x series if at all possible. (And I understand that it may not be possible.. just be careful) – Michael Anderson Oct 17 '11 at 10:03
  • I know it is buggy... I have to adapt my templated code to have it compile. I try to keep the C++ code simple and test it carefully. – Nikko Oct 17 '11 at 10:07

5 Answers5

9

if you don't need mixing shared and weak ptr,and don't need coustom deletors, you can just use the quick and dirty my_shared_ptr:

template<class T>
class my_shared_ptr
{
    template<class U>
    friend class my_shared_ptr;
public:
    my_shared_ptr() :p(), c() {}
    explicit my_shared_ptr(T* s) :p(s), c(new unsigned(1)) {}

    my_shared_ptr(const my_shared_ptr& s) :p(s.p), c(s.c) { if(c) ++*c; }

    my_shared_ptr& operator=(const my_shared_ptr& s) 
    { if(this!=&s) { clear(); p=s.p; c=s.c; if(c) ++*c; } return *this; }

    template<class U>
    my_shared_ptr(const my_shared_ptr<U>& s) :p(s.p), c(s.c) { if(c) ++*c; }

    ~my_shared_ptr() { clear(); }

    void clear() 
    { 
        if(c)
        {
            if(*c==1) delete p; 
            if(!--*c) delete c; 
        } 
        c=0; p=0; 
    }

    T* get() const { return (c)? p: 0; }
    T* operator->() const { return get(); }
    T& operator*() const { return *get(); }

private:
    T* p;
    unsigned* c;
}

For anyone interested in make_my_shared<X>, it can be trivially implemented as

template<class T, class... U>
auto make_my_shared(U&&... u)
{ 
    return my_shared_ptr<T>(new T{std::forward<U>(u)...});
}

to be called as

auto pt = make_my_shared<T>( ... );
Emilio Garavaglia
  • 20,229
  • 2
  • 46
  • 63
  • @Nikko: Always ... before boost::shared_ptr was invented! (frankly: if you don't trust, test it: two constructor one destructor and one assignment can be proved in minutes! ) – Emilio Garavaglia Oct 17 '11 at 12:29
  • 1
    I see there is also a lightweight implementation of boost::shared_ptr in boost, in shared_ptr_nmt.hpp . 'nmt' stands for "no member templates". – Nikko Oct 20 '11 at 12:49
  • Also this compiler (GCC 2.95) does not accept friend member templates, so I have to expose some private parts to have it work with different shared_ptr types – Nikko Oct 20 '11 at 13:12
  • @Nikko: the only reason I declared friendship between ptr and ptr is to allow the implicit conversion ptr(const ptr&). If you don't need such kind of conversions, no friendship is required. – Emilio Garavaglia Oct 20 '11 at 19:40
  • In what way is this class "dirty" ? Thank you :) – Virus721 Dec 17 '14 at 20:55
  • 1
    @Virus721: "Quick and Dirty" is an idiom (http://en.wikipedia.org/wiki/Quick-and-dirty): it means "made easy" for "what it is".It can't be enriched with policies about memory management,allocators, array management etc., since it has no "plugs" for anything like that. It does clearly what it does, but nothing more than that. In this context is used ironically. – Emilio Garavaglia Dec 18 '14 at 07:23
  • @EmilioGaravaglia I know this answer is 5+ years old, but how could you implement a "Quick and dirty" my_make_shared for this shared_ptr implementation? Thanks. – Valmir Feb 03 '17 at 15:46
  • 1
    @Valmir Edited - see at bottom – Emilio Garavaglia Feb 04 '17 at 15:41
  • @EmilioGaravaglia Thank you :) – Valmir Feb 10 '17 at 10:56
  • @EmilioGaravaglia Sorry, I had not paid attention to your response before, but is it only possible using C++11 stuffs (variadic templates, 'auto', etc..)? I ask this because I'm thinking use this awesome piece of code in an cross system, but unfortunately its toolchain is C++98 only. – Valmir Feb 10 '17 at 11:30
  • @Valmir: yes ... just use explicit types! auto is just a shortcut, and varadics just a commodity. – Emilio Garavaglia Feb 10 '17 at 22:24
  • @EmilioGaravaglia Thanks, I'm going to try this. – Valmir Feb 13 '17 at 10:33
  • @wavemode Yes. I just wrote this before std::atomic became standard – Emilio Garavaglia Nov 09 '17 at 20:02
4

There is also std::tr1::shared_ptr which is just C++11 standard's lifting from boost. You could pick that if it is allowed, or write your own with reference counting.

3

Which "boost overheads" are you concerned about, and in what way is shared_ptr "too heavy" for your application? There are no overheads simply from using Boost (at least if you only use header-only libraries, such as the smart pointer library); the only overheads I can think of concerning shared_ptr are:

  • Thread-safe reference counting; since you're using an ancient compiler, I assume you also have an ancient runtime library and kernel, so this may be very inefficient. If your application is single threaded, then you can disable this by #defining BOOST_DISABLE_THREADS.
  • Allocation of the reference-counting structure alongside the managed object. You can eliminate the extra allocation by creating the object using make_shared() or allocate_shared().

In many cases, you can eliminate the overhead from speed-critical code by not creating objects or copying shared pointers - pass pointers by reference and only copy them when you actually need to.

If you need thread safety for some pointers, but not others, and (after profiling, and removing all unnecessary allocations and pointer copying) you find that the use of shared pointers is still causing significant overhead, then you could consider using intrusive_ptr, and manage your own reference counting within the object.

There may also be benefit to updating to a modern GNU/Linux version, if that's feasible. Thread synchronisation is much more efficient since the introduction of futexes in Linux 2.6. You may find this helps in other ways too; there have been many improvements over the last decade. A more modern compiler would also provide the standard (TR1 or C++11) shared pointer, so you wouldn't need Boost for that.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • 2
    If I extract boost::shared_ptr with bcp I have quite a lot of headers ( more than 30 ) and two .cpp – Nikko Oct 17 '11 at 10:39
  • 3
    The source files shouldn't be necessary - I've no idea what they are (tests, perhaps), but the smart pointer library is header only. Just put the header files in your include path, and include `boost/shared_ptr.hpp`. – Mike Seymour Oct 17 '11 at 10:46
  • 4
    The header bloat problem is exactly why my company has stayed away from boost. Having to bring in 30+ header files just for a simple shared pointer library is ludicrous. And this example is an exception to the rule...most boost modules bring in SIGNIFICANTLY MORE than 30 header files. I don't understand an architecture that is so co-dependent on itself for doing simple things. – Steve May 31 '12 at 19:50
  • 1
    BTW, before someone says that it doesn't matter how many headers are included since today's compilers are so fast and very little of it is actually used...Much of our reason for caring about this header bloat is our clients' worries about copyright issues and therefore our ability to provide some level of indemnification. All of these co-dependencies lead to a lot more code written by different people. This greatly enhances the chance that someone may some day say that a piece of code we're using in boost was stolen from one of their independent works, and is infringing. – Steve May 31 '12 at 19:55
3

I'd suggest you can use shared_ptr in isolation. However, if you are looking for simple implementation

  • with some unit tests
  • not thread safe
  • basic support for polymorphic assignment <-- let me know if you're intrested
  • custom deletors (i.e. array deletors, or custom functors for special resources)

Have a look here: Creating a non-thread safe shared_ptr

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
1

You can use shared_ptr without all the Boost overhead: it's a header-only implementation. If you don't use anything other classes, only shared_ptr will be compiled in.

The implementation of shared_ptr is already pretty lean, but if you want to avoid the intermediate reference count block and a (potential) virtual call to deleter function, you can use boost::intrusive_ptr instead, which is more suited for embedded environments: it operates on a reference counter embedded in the object itself, you just have to provide a couple of functions to increment/decrement it. The downside is that you won't be able to use weak_ptr.

I can't comment how well gcc 2.95 would inline/fold template instantiations (it's a very old compiler), more recent versions of gcc handle it rather well, so you are on your own here.

Alex B
  • 82,554
  • 44
  • 203
  • 280
  • 2
    I stopped counting after 30 include files when I extract boost::shared_ptr with bcp – Nikko Oct 17 '11 at 10:43
  • 2
    @Nikko: why do you care how many header files there are? Are you trying to build your project on a tiny embedded device? – Mike Seymour Oct 17 '11 at 10:50
  • @MikeSeymour It's in an embedded system that needs small binaries and that is using an old c++ compiler that does not like complex templated codes – Nikko Oct 17 '11 at 13:27