Skip to content

Commit f8d31d4

Browse files
committed
Add Boost.Locale patch working around build issue (missing symbols) on MacOS/clang
1 parent da2042c commit f8d31d4

File tree

2 files changed

+336
-0
lines changed

2 files changed

+336
-0
lines changed

recipe/meta.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ source:
2121
patches:
2222
# ensure our compiler flags get used during bootstrapping
2323
- patches/0001-Add-default-value-for-cxx-and-cxxflags-options-for-t.patch
24+
- patches/0002-Reimplement-string_set-as-any_string.patch
2425

2526
build:
2627
number: 2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
From c5e8f02c903696a213fc4b710f6740ccd1f07f4e Mon Sep 17 00:00:00 2001
2+
From: Alexander Grund <[email protected]>
3+
Date: Sun, 3 Dec 2023 20:06:27 +0100
4+
Subject: [PATCH] Reimplement `string_set` as `any_string`
5+
6+
Use a better name for a type-erased string implemented using
7+
`dynamic_cast` instead of storing&comparing the `typeid` of the char
8+
type used.
9+
This is a workaround for missing typeinfo for `char8_t` on e.g. libc++
10+
on FreeBSD 13.1.
11+
It also simplifies memory management size calc/copy implementation.
12+
---
13+
boost/locale/detail/any_string.hpp | 71 ++++++++++++++++++++++
14+
boost/locale/formatting.hpp | 51 ++--------------
15+
libs/locale/src/boost/locale/shared/formatting.cpp | 45 --------------
16+
libs/locale/test/test_ios_info.cpp | 64 +++++++++++++++----
17+
4 files changed, 127 insertions(+), 104 deletions(-)
18+
create mode 100644 boost/locale/detail/any_string.hpp
19+
20+
diff --git a/boost/locale/detail/any_string.hpp b/boost/locale/detail/any_string.hpp
21+
new file mode 100644
22+
index 00000000..c0cc7ffb
23+
--- /dev/null
24+
+++ b/boost/locale/detail/any_string.hpp
25+
@@ -0,0 +1,71 @@
26+
+//
27+
+// Copyright (c) 2023 Alexander Grund
28+
+//
29+
+// Distributed under the Boost Software License, Version 1.0.
30+
+// https://www.boost.org/LICENSE_1_0.txt
31+
+
32+
+#ifndef BOOST_LOCALE_DETAIL_ANY_STRING_HPP_INCLUDED
33+
+#define BOOST_LOCALE_DETAIL_ANY_STRING_HPP_INCLUDED
34+
+
35+
+#include <boost/locale/config.hpp>
36+
+#include <boost/assert.hpp>
37+
+#include <boost/utility/string_view.hpp>
38+
+#include <memory>
39+
+#include <stdexcept>
40+
+#include <string>
41+
+
42+
+/// \cond INTERNAL
43+
+namespace boost { namespace locale { namespace detail {
44+
+ /// Type-erased std::basic_string
45+
+ class any_string {
46+
+ struct BOOST_SYMBOL_VISIBLE base {
47+
+ virtual ~base() = default;
48+
+ virtual base* clone() const = 0;
49+
+
50+
+ protected:
51+
+ base() = default;
52+
+ base(const base&) = default;
53+
+ base(base&&) = delete;
54+
+ base& operator=(const base&) = default;
55+
+ base& operator=(base&&) = delete;
56+
+ };
57+
+ template<typename Char>
58+
+ struct BOOST_SYMBOL_VISIBLE impl : base {
59+
+ explicit impl(const boost::basic_string_view<Char> value) : s(value) {}
60+
+ impl* clone() const override { return new impl(*this); }
61+
+ std::basic_string<Char> s;
62+
+ };
63+
+
64+
+ std::unique_ptr<const base> s_;
65+
+
66+
+ public:
67+
+ any_string() = default;
68+
+ any_string(const any_string& other) : s_(other.s_ ? other.s_->clone() : nullptr) {}
69+
+ any_string(any_string&&) = default;
70+
+ any_string& operator=(any_string other) // Covers the copy and move assignment
71+
+ {
72+
+ s_.swap(other.s_);
73+
+ return *this;
74+
+ }
75+
+
76+
+ template<typename Char>
77+
+ void set(const boost::basic_string_view<Char> s)
78+
+ {
79+
+ BOOST_ASSERT(!s.empty());
80+
+ s_.reset(new impl<Char>(s));
81+
+ }
82+
+
83+
+ template<typename Char>
84+
+ std::basic_string<Char> get() const
85+
+ {
86+
+ if(!s_)
87+
+ throw std::bad_cast();
88+
+ return dynamic_cast<const impl<Char>&>(*s_).s;
89+
+ }
90+
+ };
91+
+
92+
+}}} // namespace boost::locale::detail
93+
+
94+
+/// \endcond
95+
+
96+
+#endif
97+
diff --git a/boost/locale/formatting.hpp b/boost/locale/formatting.hpp
98+
index d14e6f69..e3c8619e 100644
99+
--- a/boost/locale/formatting.hpp
100+
+++ b/boost/locale/formatting.hpp
101+
@@ -8,15 +8,13 @@
102+
#ifndef BOOST_LOCALE_FORMATTING_HPP_INCLUDED
103+
#define BOOST_LOCALE_FORMATTING_HPP_INCLUDED
104+
105+
+#include <boost/locale/detail/any_string.hpp>
106+
#include <boost/locale/time_zone.hpp>
107+
-#include <boost/assert.hpp>
108+
-#include <boost/utility/string_view.hpp>
109+
#include <cstdint>
110+
#include <cstring>
111+
#include <istream>
112+
#include <ostream>
113+
#include <string>
114+
-#include <typeinfo>
115+
116+
#ifdef BOOST_MSVC
117+
# pragma warning(push)
118+
@@ -130,13 +128,13 @@ namespace boost { namespace locale {
119+
template<typename CharType>
120+
void date_time_pattern(const std::basic_string<CharType>& str)
121+
{
122+
- date_time_pattern_set().set<CharType>(str);
123+
+ datetime_.set<CharType>(str);
124+
}
125+
/// Get date/time pattern (strftime like)
126+
template<typename CharType>
127+
std::basic_string<CharType> date_time_pattern() const
128+
{
129+
- return date_time_pattern_set().get<CharType>();
130+
+ return datetime_.get<CharType>();
131+
}
132+
133+
/// \cond INTERNAL
134+
@@ -144,51 +142,10 @@ namespace boost { namespace locale {
135+
/// \endcond
136+
137+
private:
138+
- class string_set;
139+
-
140+
- const string_set& date_time_pattern_set() const;
141+
- string_set& date_time_pattern_set();
142+
-
143+
- class BOOST_LOCALE_DECL string_set {
144+
- public:
145+
- string_set();
146+
- ~string_set();
147+
- string_set(const string_set& other);
148+
- string_set& operator=(string_set other);
149+
- void swap(string_set& other);
150+
-
151+
- template<typename Char>
152+
- void set(const boost::basic_string_view<Char> s)
153+
- {
154+
- BOOST_ASSERT(!s.empty());
155+
- delete[] ptr;
156+
- ptr = nullptr;
157+
- type = &typeid(Char);
158+
- size = sizeof(Char) * s.size();
159+
- ptr = size ? new char[size] : nullptr;
160+
- memcpy(ptr, s.data(), size);
161+
- }
162+
-
163+
- template<typename Char>
164+
- std::basic_string<Char> get() const
165+
- {
166+
- if(type == nullptr || *type != typeid(Char))
167+
- throw std::bad_cast();
168+
- std::basic_string<Char> result(size / sizeof(Char), Char(0));
169+
- memcpy(&result.front(), ptr, size);
170+
- return result;
171+
- }
172+
-
173+
- private:
174+
- const std::type_info* type;
175+
- size_t size;
176+
- char* ptr;
177+
- };
178+
-
179+
uint64_t flags_;
180+
int domain_id_;
181+
std::string time_zone_;
182+
- string_set datetime_;
183+
+ detail::any_string datetime_;
184+
};
185+
186+
/// \brief This namespace includes all manipulators that can be used on IO streams
187+
diff --git a/libs/locale/src/boost/locale/shared/formatting.cpp b/libs/locale/src/boost/locale/shared/formatting.cpp
188+
index 489d1fd5..457ba782 100644
189+
--- a/libs/locale/src/boost/locale/shared/formatting.cpp
190+
+++ b/libs/locale/src/boost/locale/shared/formatting.cpp
191+
@@ -7,43 +7,8 @@
192+
#include <boost/locale/date_time.hpp>
193+
#include <boost/locale/formatting.hpp>
194+
#include "boost/locale/shared/ios_prop.hpp"
195+
-#include <algorithm>
196+
-#include <typeinfo>
197+
198+
namespace boost { namespace locale {
199+
-
200+
- ios_info::string_set::string_set() : type(nullptr), size(0), ptr(nullptr) {}
201+
- ios_info::string_set::~string_set()
202+
- {
203+
- delete[] ptr;
204+
- }
205+
- ios_info::string_set::string_set(const string_set& other)
206+
- {
207+
- if(other.ptr != nullptr) {
208+
- ptr = new char[other.size];
209+
- size = other.size;
210+
- type = other.type;
211+
- memcpy(ptr, other.ptr, size);
212+
- } else {
213+
- ptr = nullptr;
214+
- size = 0;
215+
- type = nullptr;
216+
- }
217+
- }
218+
-
219+
- void ios_info::string_set::swap(string_set& other)
220+
- {
221+
- std::swap(type, other.type);
222+
- std::swap(size, other.size);
223+
- std::swap(ptr, other.ptr);
224+
- }
225+
-
226+
- ios_info::string_set& ios_info::string_set::operator=(string_set other)
227+
- {
228+
- swap(other);
229+
- return *this;
230+
- }
231+
-
232+
ios_info::ios_info() : flags_(0), domain_id_(0), time_zone_(time_zone::global()) {}
233+
234+
ios_info::~ios_info() = default;
235+
@@ -105,16 +70,6 @@ namespace boost { namespace locale {
236+
return time_zone_;
237+
}
238+
239+
- const ios_info::string_set& ios_info::date_time_pattern_set() const
240+
- {
241+
- return datetime_;
242+
- }
243+
-
244+
- ios_info::string_set& ios_info::date_time_pattern_set()
245+
- {
246+
- return datetime_;
247+
- }
248+
-
249+
ios_info& ios_info::get(std::ios_base& ios)
250+
{
251+
return impl::ios_prop<ios_info>::get(ios);
252+
diff --git a/libs/locale/test/test_ios_info.cpp b/libs/locale/test/test_ios_info.cpp
253+
index 9b63aaed..79179a8f 100644
254+
--- a/libs/locale/test/test_ios_info.cpp
255+
+++ b/libs/locale/test/test_ios_info.cpp
256+
@@ -105,18 +105,6 @@ void test_member_methods()
257+
258+
info.date_time_pattern(std::string("Pattern"));
259+
TEST_EQ(info.date_time_pattern<char>(), "Pattern");
260+
-
261+
- info.date_time_pattern(ascii_to<wchar_t>("WChar Pattern"));
262+
- TEST_EQ(info.date_time_pattern<wchar_t>(), ascii_to<wchar_t>("WChar Pattern"));
263+
- TEST_THROWS(info.date_time_pattern<char>(), std::bad_cast);
264+
-
265+
- info.date_time_pattern(ascii_to<char16_t>("Char16 Pattern"));
266+
- TEST_THROWS(info.date_time_pattern<wchar_t>(), std::bad_cast);
267+
- TEST_EQ(info.date_time_pattern<char16_t>(), ascii_to<char16_t>("Char16 Pattern"));
268+
-
269+
- info.date_time_pattern(ascii_to<char32_t>("Char32 Pattern"));
270+
- TEST_THROWS(info.date_time_pattern<char16_t>(), std::bad_cast);
271+
- TEST_EQ(info.date_time_pattern<char32_t>(), ascii_to<char32_t>("Char32 Pattern"));
272+
}
273+
}
274+
275+
@@ -212,8 +200,60 @@ void test_manipulators()
276+
TEST_EQ(info2.date_time_pattern<wchar_t>(), L"My TZ");
277+
}
278+
279+
+void test_any_string()
280+
+{
281+
+ boost::locale::detail::any_string s;
282+
+ TEST_THROWS(s.get<char>(), std::bad_cast);
283+
+ TEST_THROWS(s.get<wchar_t>(), std::bad_cast);
284+
+
285+
+ s.set<char>("Char Pattern");
286+
+ TEST_EQ(s.get<char>(), "Char Pattern");
287+
+ TEST_THROWS(s.get<wchar_t>(), std::bad_cast);
288+
+
289+
+ s.set<wchar_t>(ascii_to<wchar_t>("WChar Pattern"));
290+
+ TEST_EQ(s.get<wchar_t>(), ascii_to<wchar_t>("WChar Pattern"));
291+
+ TEST_THROWS(s.get<char>(), std::bad_cast);
292+
+
293+
+ s.set<char16_t>(ascii_to<char16_t>("Char16 Pattern"));
294+
+ TEST_EQ(s.get<char16_t>(), ascii_to<char16_t>("Char16 Pattern"));
295+
+ TEST_THROWS(s.get<char>(), std::bad_cast);
296+
+
297+
+ s.set<char32_t>(ascii_to<char32_t>("Char32 Pattern"));
298+
+ TEST_EQ(s.get<char32_t>(), ascii_to<char32_t>("Char32 Pattern"));
299+
+ TEST_THROWS(s.get<char16_t>(), std::bad_cast);
300+
+
301+
+#ifndef BOOST_LOCALE_NO_CXX20_STRING8
302+
+ s.set<char8_t>(ascii_to<char8_t>("Char8 Pattern"));
303+
+ TEST_EQ(s.get<char8_t>(), ascii_to<char8_t>("Char8 Pattern"));
304+
+ TEST_THROWS(s.get<char32_t>(), std::bad_cast);
305+
+#endif
306+
+
307+
+ boost::locale::detail::any_string s1, s2, empty;
308+
+ s1.set<char>("Char");
309+
+ s2.set<wchar_t>(ascii_to<wchar_t>("WChar"));
310+
+ // Copy ctor
311+
+ boost::locale::detail::any_string s3(s1);
312+
+ TEST_EQ(s3.get<char>(), "Char");
313+
+ TEST_EQ(s1.get<char>(), "Char");
314+
+ // Ensure deep copy
315+
+ s3.set<char>("Foo");
316+
+ TEST_EQ(s3.get<char>(), "Foo");
317+
+ TEST_EQ(s1.get<char>(), "Char");
318+
+ // Copy assign
319+
+ s3 = s2;
320+
+ TEST_EQ(s3.get<wchar_t>(), ascii_to<wchar_t>("WChar"));
321+
+ TEST_EQ(s2.get<wchar_t>(), ascii_to<wchar_t>("WChar"));
322+
+ // Move assign
323+
+ s3 = std::move(s1);
324+
+ TEST_EQ(s3.get<char>(), "Char");
325+
+ // From empty
326+
+ s3 = empty;
327+
+ TEST_THROWS(s3.get<char>(), std::bad_cast);
328+
+}
329+
+
330+
void test_main(int /*argc*/, char** /*argv*/)
331+
{
332+
+ test_any_string();
333+
test_member_methods();
334+
test_manipulators();
335+
}

0 commit comments

Comments
 (0)