openframeworks / openFrameworks

openFrameworks is a community-developed cross platform toolkit for creative coding in C++.
http://openframeworks.cc
Other
9.97k stars 2.55k forks source link

bugfix : c++11 on xcode #2335

Closed elliotwoods closed 10 years ago

elliotwoods commented 11 years ago

Hey all!

[EDIT] It seems that we don't have c++11 support right now with on osx. The issue primarily is that we have to use libc++ with c++11, which does not support legacy tr1 namespace symbols. To fix this bug we need to write a c++11 version which does not use tr1 namespace.

This could either involve:

  1. Writing a win/linux/mac C++11 implementation without tr1
  2. Writing a special implementation for libc++ without tr1 for osx

I think the only difference between 1 and 2 is more testing is required for 1. (i.e. the code should look identical).

[/EDIT]

In ofTypes I edited:

#if (_MSC_VER)
#include <memory>
#else
#include <tr1/memory>
// import smart pointers utils into std
namespace std {
    using std::tr1::shared_ptr;
    using std::tr1::weak_ptr;
    using std::tr1::static_pointer_cast;
    using std::tr1::dynamic_pointer_cast;
    using std::tr1::const_pointer_cast;
    using std::tr1::enable_shared_from_this;
    using std::tr1::__dynamic_cast_tag;
}
#endif

to:

#if (_MSC_VER || true) // <----basically hacked here
#include <memory>
using std::shared_ptr;
#else
#include <tr1/memory>
// import smart pointers utils into std
namespace std {
    using std::tr1::shared_ptr;
    using std::tr1::weak_ptr;
    using std::tr1::static_pointer_cast;
    using std::tr1::dynamic_pointer_cast;
    using std::tr1::const_pointer_cast;
    using std::tr1::enable_shared_from_this;
    using std::tr1::__dynamic_cast_tag;
}
#endif

now i get complaints on __dynamic_cast_tag. And ran out of google results. ideas?

/Volumes/SHARED/openFrameworks/libs/openFrameworks/types/ofTypes.h:169:36: No type named '__dynamic_cast_tag' in namespace 'std'
bilderbuchi commented 11 years ago

I'd start with this interesring if statement: #if (_MSC_VER || true) ;-) you can never enter the else clause. also, pinging @leocolomb about this, I think he was the author of this passage. edit: wait, what? hmmm

elliotwoods commented 11 years ago

:) that's the bit i hacked basically the VS2012 code seems to be proper C++11, whilst the rest is using tr1 therefore the switch should really be looking for C++11, not vs2012

bilderbuchi commented 11 years ago

the switch should really be looking for C++11, not vs2012

on a second look I think I agree. but wouldn't the "proper" hack be to test for C++11 instead of true? :-) also, @tgfrerer could know what's the proper course here.

bilderbuchi commented 11 years ago

shot in the dark: could be a libc++ thing? http://stackoverflow.com/questions/7016730/compiling-with-clang-using-libc-undefined-references

bilderbuchi commented 11 years ago

wait, something is iffy. in your "original" version, where's the #if __cplusplus<201103L clause which is in current develop?

elliotwoods commented 11 years ago

ooh! i thought i was on recent develop since yesterday. i must have missed something. trying now!

bilderbuchi commented 11 years ago

OK possibly the problem here is just the tr1/memory include, which I think needs to select for C++11. Probably like this

#if __cplusplus < 201103L
#include <tr1/memory>
#else
#include <memory>
#endif

or so.

elliotwoods commented 11 years ago

to notes __cplusplus < 201103L == true on xcode/libc++ c++11

[EDIT] This is only true when using stdlibc++ becuase that doesn't have c++11 support. So this statement isn't entirely correct. [/EDIT]

bilderbuchi commented 11 years ago

what's the actual value of __cplusplus?

elliotwoods commented 11 years ago

the new code still doesn't work, also we shouldn't be using tr1 at all

#if __cplusplus<201103L
    using std::tr1::shared_ptr;
    using std::tr1::weak_ptr;
    using std::tr1::enable_shared_from_this;
#endif
    using std::tr1::static_pointer_cast;
    using std::tr1::dynamic_pointer_cast;
    using std::tr1::const_pointer_cast;
    using std::tr1::__dynamic_cast_tag;
}

still doesn't have a c++11 code path

elliotwoods commented 11 years ago

answers here to __cplusplus: http://stackoverflow.com/questions/12865226/in-xcode-4-5-what-is-compiler-default-for-c-standard-library-and-c-lan

bilderbuchi commented 11 years ago

It could be that this worked for now (i.e. with C++11 and stdlibc++) because according to the article I linked above, "Some standard library vendors have maintained the tr1 header files for backwards compatibility. Other vendors (such as libc++, which being c++11 only, has no backwards compatibility to worry about) do not." So now that you are using libc++ this may have gotten exposed.

bilderbuchi commented 11 years ago

answers here to __cplusplus

that's not really an answer, though. what do you get when you run the program given in the accepted answer? for kicks, could you add a std::cout << __cplusplus << std::endl;, from here?

elliotwoods commented 11 years ago

I have to setup a separate app (oF wont run in this configuration), and the result is 201103, which tallies with what we're currently using.

elliotwoods commented 11 years ago

to confirm, libc++ is required for using std::function.

bilderbuchi commented 11 years ago

to confirm, libc++ is required for using std::function.

you mean C++11, not libc++, right? std:function is also in stdlibc++. or is this not available on macos?

I'm sorry I can't help better, but without having macos or libc++ available, it's a bit hard to troubleshoot.

bilderbuchi commented 11 years ago

To summarize my thoughts, I think we have two (independent) issues here:

  1. For some reason, for cplusplus < 201103L == true on OF xcode/libc++ C++11, while on a standalone program on the same machine cplusplus=201103 as expected. This throws off our include logic for sure, and we should find out why it's wrong.
  2. saying using std::tr1::static_pointer_cast; etc. in a C++11 environment is strictly incorrect, but apparently OK when using some implementations (i.e. stdlibc++), but not others which don't provide backwards compatibility (i.e. libc++).
elliotwoods commented 11 years ago

Correct re:not available on mac os, sorry to send those emails. On XCode, libc++ is required for C++11: image

elliotwoods commented 11 years ago

To confirm:

  1. With oF / libc++ / XCode 4 / C++11, __cpluplus == 201103L
  2. For C++11 support on osx, we can't use tr1 namespace as libc++ is the only available library for this version.

I'll edit the bug at the top.

bilderbuchi commented 11 years ago

thanks for the follow up. I'm wondering, with

  1. With oF / libc++ / XCode 4 / C++11, __cpluplus == 201103L

how was this possible:?

to notes __cplusplus < 201103L == true on xcode/libc++ c++11

was there a configuration change or so between those two observations/comments?

elliotwoods commented 11 years ago

I've updated the note above. I was getting __cplusplus == 201103L with libc++, and < 201103L with stdlibc++

bilderbuchi commented 11 years ago

I got a saner ifdef logic running nearly running on my machine now, with the exception of the dynamic_cast_tag which does not seem to exist on C++11 anymore. @tgfrerer, who introduced this in 77123775561a11058de33af7e9ba70c2c549abbf: what is this about, and how do we make it work on C++11/libc++? (without touching TR1 I guess) A google search primarily points back to OF code ;-) I can't find any reference to it outside of tr/shared_ptr.h.

elliotwoods commented 11 years ago

Yep, changing the top section to:

#if (_MSC_VER || _LIBCPP_VERSION)
#include <memory>
#else

basically fixes everything except dynamic_tag

bilderbuchi commented 11 years ago

What I got currently works for me on ubuntu/stdlibc++ except the dynamic_cast_tag. If we find out why VS wants different include path in C++98, we might even be able to get rid of that, too.

#if (_MSC_VER)
    #include <memory>
#else
    #if __cplusplus<201103L
        #include <tr1/memory>
        // import smart pointers utils into std
        namespace std {
            using std::tr1::shared_ptr;
            using std::tr1::weak_ptr;
            using std::tr1::enable_shared_from_this;
            using std::tr1::static_pointer_cast;
            using std::tr1::dynamic_pointer_cast;
            using std::tr1::const_pointer_cast;
            using std::tr1::__dynamic_cast_tag;
        }
    #else
        #include <memory>
        //still have to deal with __dynamic_cast_tag here!
    #endif
#endif
elliotwoods commented 11 years ago

This hack temporarily works:

#if (_MSC_VER)
    template<typename Tp1>
    ofPtr(const ofPtr<Tp1>& __r, std::_Dynamic_tag)
    : std::shared_ptr<T>(__r, std:::_Dynamic_tag()) { }
#elif (!_LIBCPP_VERSION)
    template<typename Tp1>
    ofPtr(const ofPtr<Tp1>& __r, std::__dynamic_cast_tag)
    : std::shared_ptr<T>(__r, std::__dynamic_cast_tag()) { }
#endif

Basically knock out the dynamic_tag bits when using LIBC

I presume there's a different approach to dynamic casting in c++11 in general.

bilderbuchi commented 11 years ago

but are you then not missing this template version/declaration on libc++, cause you get neither the MSC nor the "normal" version?

bilderbuchi commented 11 years ago

I'm guessing the C++11 way is via dynamic_pointer_cast http://www.cplusplus.com/reference/memory/dynamic_pointer_cast/

elliotwoods commented 11 years ago

I'm sorry about the confusion before This was because i wasn't setting the C++ dialect to C++11 : image

Now it compiles, but bombs out on linking, with basically the entire standard library under complaint: https://gist.github.com/elliotwoods/6081901 (clean rebuilds don't help) Something else is going on here.

My app has the c++11 settings in the screenshot, and it doesn't matter if i compile openFrameworks with or without those flags set, i get the same linker errors (thought it might be a mismatch issue. might still be a mismatch issue with poco or something)

LeoColomb commented 11 years ago

I add my notes, because I have been pinged:

With your last code, it should work with:

#if (__cplusplus<201103L && !(_MSC_VER))
#include <tr1/memory>
  // import smart pointers utils into std
  namespace std {
      using std::tr1::shared_ptr;
      using std::tr1::weak_ptr;
      using std::tr1::enable_shared_from_this;
      using std::tr1::static_pointer_cast;
      using std::tr1::dynamic_pointer_cast;
      using std::tr1::const_pointer_cast;
      using std::tr1::__dynamic_cast_tag;
  }
#else
#include <memory>
  //still have to deal with __dynamic_cast_tag here!
#endif

EDIT: Also, see this question.

tgfrerer commented 11 years ago

dynamic_pointer_cast allows you to cast an ofPtr from its parent type to one of its derived types, just like dynamic_cast (which is the preferred c++ style of dynamic pointer casting) does for raw pointers.

dyamic_cast_tag was an internal flag that some c++11 implementations used to signal that the shared ptr was going to be re-cast, and this is where it gets ugly, since some compilers/implementations used this flags, some didn't, and ofPtr, being essentially a façade around shared_ptr had to take all this into account.

in the short term, i'd suggest taking out the dynamic_pointer_cast functionality from ofPtr, since it does more bad than good, there real-life use-cases are rare and far-between, and it's proved being a maintainability nightmare.

in the long term, i'd suggest deprecating ofPtr in favour of shared_ptr and unique_ptr, since that will also give us more compatibility towards other libraries.

bilderbuchi commented 11 years ago

thanks for clearing this up @tgfrerer! So, to make your short-term fix happen, we can just rip out https://github.com/openframeworks/openFrameworks/blob/develop/libs/openFrameworks/types/ofTypes.h#L186-L194 and https://github.com/openframeworks/openFrameworks/blob/develop/libs/openFrameworks/types/ofTypes.h#L205-L216, correct? Will this break project or have other negative repercussions we should note in the release notes etc?

elliotwoods commented 11 years ago

To confirm, would this disallow casting an ofPtr of a class to/from a parent type from/to a child type?

tgfrerer commented 11 years ago

it would.

but then, you could always use a hack - grabbing the raw pointer from within ofPtr and re-casting that using raw pointer cast methods.

the preferred method for dynamic casting shared pointers would be to use shared_ptr instead of ofPtr in these cases, ofPtr doesn't really give you that much (apart from cross-compiler headaches) in more advanced use-cases, and with the advent of c++11 it will be much easier and future-proof to stick with the standard unique_ptr and shared_ptr.

elliotwoods commented 11 years ago

ok understood. can we therefore rewrite ofPtr for c++11 to wrap shared_ptr?

tgfrerer commented 11 years ago

ofPtr already wraps shared_ptr =) but it's this wrapping - or better put: the façade design pattern - that causes the complications.

i think ofPtr was introduced to have a compiler and c++ version independent wrapper around shared_ptr. But now that shared_ptr has become part of the standard, why not use the real thing?

arturoc commented 11 years ago

it was even just to make it more of'ish :) i'm totally ok with removing it once we have compatibility with c++11 and use the standard. meanwhile can we perhaps deactivate the dynamic casting hack for vs on c++11 so people can still use c++11 on that platform

i guess an ifdef around that code will do

On 08/05/2013 03:50 PM, Tim Gfrerer wrote:

ofPtr already wraps shared_ptr =) but it's this wrapping - or better put: the façade design pattern - that causes the complications.

i think ofPtr was introduced to have a compiler and c++ version independent wrapper around shared_ptr. But now that shared_ptr has become part of the standard, why not use the real thing?

— Reply to this email directly or view it on GitHub https://github.com/openframeworks/openFrameworks/issues/2335#issuecomment-22107100.

tgfrerer commented 11 years ago

=) let's do it.

elliotwoods commented 11 years ago

C++11 works fine with VS and oF right now. Xcode is issue here

arturoc commented 11 years ago

oh, ok, osx then :)

On 08/05/2013 04:01 PM, Elliot Woods wrote:

C++11 works fine with VS and oF right now. Xcode is issue here

Typed by touchscreen

On 5 Aug 2013, at 14:59, arturo notifications@github.com wrote:

it was even just to make it more of'ish :) i'm totally ok with removing it once we have compatibility with c++11 and use the standard. meanwhile can we perhaps deactivate the dynamic casting hack for vs on c++11 so people can still use c++11 on that platform

i guess an ifdef around that code will do

On 08/05/2013 03:50 PM, Tim Gfrerer wrote:

ofPtr already wraps shared_ptr =) but it's this wrapping - or better put: the façade design pattern - that causes the complications.

i think ofPtr was introduced to have a compiler and c++ version independent wrapper around shared_ptr. But now that shared_ptr has become part of the standard, why not use the real thing?

— Reply to this email directly or view it on GitHub < https://github.com/openframeworks/openFrameworks/issues/2335#issuecomment-22107100>.

— Reply to this email directly or view it on GitHubhttps://github.com/openframeworks/openFrameworks/issues/2335#issuecomment-22107615

.

— Reply to this email directly or view it on GitHub https://github.com/openframeworks/openFrameworks/issues/2335#issuecomment-22107812.

rc1 commented 11 years ago

+1 on getting rid of ofPtr in favour of shared_ptr if it is possible.

For someone new to oF and on a c++ learning curve, ofPtr is a bit shrouded in mystique without much accessible info (the philosophy behind it, how it works or why it's there). shared_ptr on the other hand is highly googleable.

rezaali commented 11 years ago

any updates on this issue?

bakercp commented 11 years ago

+1 to get this moving. Even if we aren't going to immediately remove ofPtr it would be really nice to get some preprocessor help to get the core / projects to compile c++11 running in the core easily.

nervoussystem commented 11 years ago

+1 Had the same issue with gcc in linux trying to use c++11 features not included in tr1 (like thread which is a super clean way to multithread functions). Got it working by eliminating dynamic_cast_tag, but it would be convenient to have a preprocessor flag to allow c++11 to work. I'll also second removing ofPtr in favor of shared_ptr. Using std library just seems better when possible.

elliotwoods commented 11 years ago

could we just typedef ofPtr to shared_ptr?

On 12 September 2013 10:48, Jesse Louis-Rosenberg notifications@github.comwrote:

+1 Had the same issue with gcc in linux trying to use c++11 features not included in tr1 (like thread which is a super clean way to multithread functions). Got it working by eliminating dynamic_cast_tag, but it would be convenient to have a preprocessor flag to allow c++11 to work. I'll also second removing ofPtr in favor of shared_ptr. Using std library just seems better when possible.

— Reply to this email directly or view it on GitHubhttps://github.com/openframeworks/openFrameworks/issues/2335#issuecomment-24289787 .

Elliot Woods elliot elliot@kimchiandchips.com@KimchiAndChips.comhttp://www.kimchiandchips.com/

tgfrerer commented 11 years ago

It's hard (if not impossible) to typedef a template.

The next idea would be to use a preprocessor macro to substitute any mentions of ofPtr with shared_ptr, but this also is not un-tricky.

That said, if there was common interest in getting c++11 to run, how about we get together and make a branch (and ultimately a PR) where we concentrate our efforts (and findings)?

bilderbuchi commented 11 years ago

forgive my ignorance, but something like this is not an option? Also, might as well wrap ofPtr in a deprecation macro at the same time, if we want to phase it out anyway.

elliotwoods commented 11 years ago

can somebody explain why we made ofPtr in the first place? it seems that there is a a little bit of confusion as to why it exists it looks prettier than shared_ptr, which I think is the only reason i use it.

elliotwoods commented 11 years ago

On that Stack Overflow post:

The catch is that nobody supports it yet. GCC has a patch available, but it probably won't make it into mainline until 4.7.

that was in 2011 (it's a C++0x feature)

I presume that compilers in the C++11 regime will be fine with it, if so, we could switch to template alias for C++11 compilers, and stick with existing tr1 implementation for previous?

tgfrerer commented 11 years ago

@elliotwoods as @arturoc explains earlier, it was made to make shared pointers more "oF-ish": https://github.com/openframeworks/openFrameworks/issues/2335#issuecomment-22107615

@bilderbuchi The key would be to make things simpler, and more standards-compliant, which is why I'm strongly in favour of ditching ofPtr. I fear the stackoverflow method adds another layer of "wrapping paper," which we don't really need.

The current problem with ofPtr imho is that it's a cross compiler maintenance headache waiting to happen, and not compatible with standards-compliant APIs that use shared_ptr. In short: it is complicated code where it doesn't need to be.

elliotwoods commented 11 years ago

cheers @tgfrerer

what i've learned from this thread:

Don't use ofPtr it's bad for business