AWS SDK for C++  1.8.8
AWS SDK for C++
AWSString.h
Go to the documentation of this file.
1 
6 #pragma once
7 
9 
11 
12 #include <functional>
13 #include <string>
14 
15 namespace Aws
16 {
17 
18 #if defined(_GLIBCXX_FULLY_DYNAMIC_STRING) && _GLIBCXX_FULLY_DYNAMIC_STRING == 0 && defined(__ANDROID__)
19 
20 /*
21 using std::string with shared libraries is broken on android when using gnustl
22 due to the platform-level decision to set _GLIBCXX_FULLY_DYNAMIC_STRING to 0
23 
24 The problem:
25 
26 (1) _GLIBCXX_FULLY_DYNAMIC_STRING is set to 0 in Android's c++config.h for gnustl
27 (2) The optimization that this enables is completely broken if using shared libraries and there is no way to opt out of using it.
28  An optimization that uses a comparison to a static memory address is death for shared libraries.
29 
30 Supposing you have a shared library B that depends on another shared library A. There are a variety of innocuous scenarios where you end up crashing
31 in the std::basic_string destructor because it's attempting to free a static memory address (&std::string::_Rep_Base::_S_empty_rep_storage -- you'll need to temporarily
32 flip the struct to "public:" in order to take this address from your code).
33 If you take the address of this location, you will get two
34 different answers depending on whether you query it in library A or library B (oddly enough, if you look the symbol itself up, it only shows up as public weak in
35 libgnustl_shared.so). When the string destructor is run from the context of library B, the _Rep::_M_dispose function will end up attempting to free
36 an address that is static memory (from library A).
37 
38 
39 Lessons (with the empty string optimization forced on you):
40  (1) You can't move std::strings across shared libraries (as a part of another class, Outcome in our case)
41  (2) If you default initialize a std::string member variable, you can't have a mismatched constructor/destructor pair such that one is in a cpp file and the other
42  is missing/implicit or defined in the header file
43 
44 Solutions:
45 
46 Use libc++ rather than gnustl
47 
48 For those who must use gnustl, we have provided a working solution by cobbling together a set of hacks:
49 
50 We prevent the empty string optimization from ever being run on our strings by:
51  (1) Make Aws::Allocator always fail equality checks with itself; this check is part of the empty string optimization in several std::basic_string constructors
52  (2) All other cases are prevented by turning Aws::String into a subclass whose default constructor and move operations go to baseclass versions which will not
53  perform the empty string optimization
54 
55 Those changes prevent crashes, but lead to very poor performance when using a string stream; every character added will result in multiple copies of the entire
56 string (ie, quadratic).
57 
58 To fix the performance problems, we have put together a set of replacement classes, SimpleStreamBuf and SimpleStringStream, that
59 replace std::stringstream and std::stringbuf in SDK code. These replacements use raw buffers rather than strings in order to
60 avoid the performance issues.
61 
62 This solution is only enabled if using gnustl on Android. In all other situations, normal STL types are used.
63 */
64 
65 using AndroidBasicString = std::basic_string< char, std::char_traits< char >, Aws::Allocator< char > >;
66 
67 class String : public AndroidBasicString
68 {
69  public:
70  using Base = AndroidBasicString;
71 
72  String() : Base("") {} // allocator comparison failure will cause empty string optimisation to be skipped
73  String(const String& rhs ) : Base(rhs) {}
74  String(String&& rhs) : Base(rhs) {} // DO NOT CALL std::move, let this go to the const ref constructor
75  String(const AndroidBasicString& rhs) : Base(rhs) {}
76  String(AndroidBasicString&& rhs) : Base(rhs) {} // DO NOT CALL std::move, let this go to the const ref constructor
77  String(const char* str) : Base(str) {}
78  String(const char* str_begin, const char* str_end) : Base(str_begin, str_end) {}
79  String(const AndroidBasicString& str, size_type pos, size_type count) : Base(str, pos, count) {}
80  String(const String& str, size_type pos, size_type count) : Base(str, pos, count) {}
81  String(const char* str, size_type count) : Base(str, count) {}
82  String(size_type count, char c) : Base(count, c) {}
83  String(std::initializer_list<char> __l) : Base(__l) {}
84 
85  template<class _InputIterator>
86  String(_InputIterator __beg, _InputIterator __end) : Base(__beg, __end) {}
87 
88  String& operator=(const String& rhs) { Base::operator=(rhs); return *this; }
89  String& operator=(String&& rhs) { Base::operator=(rhs); return *this; } // might be ok to use std::move (base class uses swap) but let's be safe
90  String& operator=(const AndroidBasicString& rhs) { Base::operator=(rhs); return *this; }
91  String& operator=(AndroidBasicString&& rhs) { Base::operator=(rhs); return *this; } // might be ok to use std::move (base class uses swap) but let's be safe
92  String& operator=(const char* str) { Base::operator=(str); return *this; }
93 };
94 
95 #else
96 
97 using String = std::basic_string< char, std::char_traits< char >, Aws::Allocator< char > >;
98 
99 #ifdef _WIN32
100 using WString = std::basic_string< wchar_t, std::char_traits< wchar_t >, Aws::Allocator< wchar_t > >;
101 #endif
102 
103 #endif // __ANDROID
104 
105 } // namespace Aws
106 
107 
108 
std::allocator< T > Allocator
Definition: AWSAllocator.h:84
std::basic_string< char, std::char_traits< char >, Aws::Allocator< char > > String
Definition: AWSString.h:97