File: RegisterString.cpp

package info (click to toggle)
jazz2-native 3.5.0-3
  • links: PTS, VCS
  • area: contrib
  • in suites: forky, sid
  • size: 16,912 kB
  • sloc: cpp: 172,557; xml: 113; python: 36; makefile: 5; sh: 2
file content (777 lines) | stat: -rw-r--r-- 29,098 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
#if defined(WITH_ANGELSCRIPT)

#include "RegisterString.h"
#include "../../Main.h"

#include "../../nCine/Base/Algorithms.h"
#include "../../nCine/Base/HashMap.h"

#include <cstring>
#include <memory>
#include <new>

#if !defined(DEATH_TARGET_ANDROID) && !defined(_WIN32_WCE) && !defined(__psp2__)
#	include <locale.h>		// setlocale()
#endif

#include <Containers/Reference.h>
#include <Containers/StringConcatenable.h>

#define AS_USE_ACCESSORS 1

using namespace Death::Containers;
using namespace Death::Containers::Literals;
using namespace nCine;

namespace Jazz2::Scripting
{
	class StringFactory : public asIStringFactory
	{
	public:
		struct InternalData {
			std::unique_ptr<String> Data;
			std::int32_t RefCount;

			InternalData(std::int32_t refCount) : RefCount(refCount) {}
		};

		StringFactory() {}

		~StringFactory()
		{
			// The script engine must release each string constant that it has requested
			DEATH_ASSERT(_stringCache.size() == 0);
		}

		const void* GetStringConstant(const char* data, std::uint32_t length)
		{
			// The string factory might be modified from multiple threads, so it is necessary to use a mutex
			asAcquireExclusiveLock();

			// Recreate non-owned string
			const String stringView(data, length, [](char*, std::size_t) {});
			auto it = _stringCache.find(stringView);
			if (it != _stringCache.end()) {
				it->second.RefCount++;;
			} else {
				auto data = std::make_unique<String>(stringView);
				it = _stringCache.emplace(*data.get(), 1).first;
				it->second.Data = std::move(data);
				//LOGD("Allocated \"{}\" string", it->second.Data.get());
			}
			asReleaseExclusiveLock();

			return static_cast<const void*>(it->second.Data.get());
		}

		int ReleaseStringConstant(const void* str)
		{
			if (str == nullptr) return asERROR;

			std::int32_t ret = asSUCCESS;

			// The string factory might be modified from multiple threads, so it is necessary to use a mutex
			asAcquireExclusiveLock();

			const String& stringView = *static_cast<const String*>(str);
			auto it = _stringCache.find(stringView);
			if (it == _stringCache.end()) {
				ret = asERROR;
			} else {
				it->second.RefCount--;
				if (it->second.RefCount == 0) {
					//LOGD("Released \"{}\" string", it->second.Data.get());
					_stringCache.erase(it);
				}
			}

			asReleaseExclusiveLock();

			return ret;
		}

		int GetRawStringData(const void* str, char* data, std::uint32_t* length) const
		{
			if (str == nullptr) return asERROR;

			const String& stringView = *static_cast<const String*>(str);
			if (length) {
				*length = (std::uint32_t)stringView.size();
			}
			if (data != nullptr) {
				std::memcpy(data, stringView.data(), stringView.size());
			}
			return asSUCCESS;
		}

		struct StringRefEqualTo
		{
			inline bool operator()(const Reference<const String>& a, const Reference<const String>& b) const noexcept {
				return a.get() == b.get();
			}
		};

		HashMap<Reference<const String>, InternalData,
#if defined(DEATH_TARGET_32BIT)
			xxHash32Func<String>,
#else
			xxHash64Func<String>,
#endif
			StringRefEqualTo> _stringCache;
	};

	static StringFactory* _stringFactory = nullptr;

	StringFactory* GetStringFactoryInstance()
	{
		if (_stringFactory == nullptr) {
			// The following instance will be destroyed by the global StringFactoryCleaner instance upon application shutdown
			_stringFactory = new StringFactory();
		}
		return _stringFactory;
	}

	class StringFactoryCleaner
	{
	public:
		~StringFactoryCleaner()
		{
			if (_stringFactory != nullptr) {
				// Only delete the string factory if the stringCache is empty. If it is not empty, it means that someone might still attempt
				// to release string constants, so if we delete the string factory, the application might crash. Not deleting the cache would
				// lead to a memory leak, but since this is only happens when the application is shutting down anyway, it is not important.
				if (_stringFactory->_stringCache.empty()) {
					delete _stringFactory;
					_stringFactory = nullptr;
				}
			}
		}
	};

	static StringFactoryCleaner cleaner;

	static void ConstructString(String* thisPointer)
	{
		new(thisPointer) String();
	}

	static void CopyConstructString(const String& other, String* thisPointer)
	{
		new(thisPointer) String(other);
	}

	static void DestructString(String* thisPointer)
	{
		thisPointer->~String();
	}

	static String& AddAssignStringToString(const String& str, String& dest)
	{
		// We don't register the method directly because some compilers and standard libraries inline the definition,
		// resulting in the linker being unable to find the declaration. Example: CLang/LLVM with XCode 4.3 on OSX 10.7
		dest += str;
		return dest;
	}

	// bool string::empty()
	static bool StringIsEmpty(const String& str)
	{
		// We don't register the method directly because some compilers
		// and standard libraries inline the definition, resulting in the
		// linker being unable to find the declaration
		// Example: CLang/LLVM with XCode 4.3 on OSX 10.7
		return str.empty();
	}

	static String& AssignUInt64ToString(std::uint64_t i, String& dest)
	{
		char tmpBuffer[32];
		u64tos(i, tmpBuffer);
		dest = String(tmpBuffer);
		return dest;
	}

	static String& AddAssignUInt64ToString(std::uint64_t i, String& dest)
	{
		char tmpBuffer[32];
		u64tos(i, tmpBuffer);
		dest += StringView(tmpBuffer);
		return dest;
	}

	static String AddStringUInt64(const String& str, std::uint64_t i)
	{
		char tmpBuffer[32];
		u64tos(i, tmpBuffer);
		return str + StringView(tmpBuffer);
	}

	static String AddInt64String(std::int64_t i, const String& str)
	{
		char tmpBuffer[32];
		i64tos(i, tmpBuffer);
		return StringView(tmpBuffer) + str;
	}

	static String& AssignInt64ToString(std::int64_t i, String& dest)
	{
		char tmpBuffer[32];
		i64tos(i, tmpBuffer);
		dest = String(tmpBuffer);
		return dest;
	}

	static String& AddAssignInt64ToString(std::int64_t i, String& dest)
	{
		char tmpBuffer[32];
		i64tos(i, tmpBuffer);
		dest += StringView(tmpBuffer);
		return dest;
	}

	static String AddStringInt64(const String& str, std::int64_t i)
	{
		char tmpBuffer[32];
		i64tos(i, tmpBuffer);
		return str + StringView(tmpBuffer);
	}

	static String AddUInt64String(std::uint64_t i, const String& str)
	{
		char tmpBuffer[32];
		u64tos(i, tmpBuffer);
		return StringView(tmpBuffer) + str;
	}

	static String& AssignDoubleToString(double f, String& dest)
	{
		char tmpBuffer[32];
		ftos(f, tmpBuffer, arraySize(tmpBuffer));
		dest = String(tmpBuffer);
		return dest;
	}

	static String& AddAssignDoubleToString(double f, String& dest)
	{
		char tmpBuffer[32];
		ftos(f, tmpBuffer, arraySize(tmpBuffer));
		dest += StringView(tmpBuffer);
		return dest;
	}

	static String& AssignFloatToString(float f, String& dest)
	{
		char tmpBuffer[32];
		ftos(f, tmpBuffer, arraySize(tmpBuffer));
		dest = String(tmpBuffer);
		return dest;
	}

	static String& AddAssignFloatToString(float f, String& dest)
	{
		char tmpBuffer[32];
		ftos(f, tmpBuffer, arraySize(tmpBuffer));
		dest += StringView(tmpBuffer);
		return dest;
	}

	static String& AssignBoolToString(bool b, String& dest)
	{
		dest = (b ? "true"_s : "false"_s);
		return dest;
	}

	static String& AddAssignBoolToString(bool b, String& dest)
	{
		dest += (b ? "true"_s : "false"_s);
		return dest;
	}

	static String AddStringDouble(const String& str, double f)
	{
		char tmpBuffer[32];
		ftos(f, tmpBuffer, arraySize(tmpBuffer));
		return str + StringView(tmpBuffer);
	}

	static String AddDoubleString(double f, const String& str)
	{
		char tmpBuffer[32];
		ftos(f, tmpBuffer, arraySize(tmpBuffer));
		return StringView(tmpBuffer) + str;
	}

	static String AddStringFloat(const String& str, float f)
	{
		char tmpBuffer[32];
		ftos(f, tmpBuffer, arraySize(tmpBuffer));
		return str + StringView(tmpBuffer);
	}

	static String AddFloatString(float f, const String& str)
	{
		char tmpBuffer[32];
		ftos(f, tmpBuffer, arraySize(tmpBuffer));
		return StringView(tmpBuffer) + str;
	}

	static String AddStringBool(const String& str, bool b)
	{
		return str + (b ? "true"_s : "false"_s);
	}

	static String AddBoolString(bool b, const String& str)
	{
		return (b ? "true"_s : "false"_s) + str;
	}

	static char* StringCharAt(unsigned int i, String& str)
	{
		if (i >= str.size()) {
			asIScriptContext* ctx = asGetActiveContext();
			ctx->SetException("Out of range");
			return nullptr;
		}

		return &str[i];
	}

	// int string::opCmp(const string &in) const
	static std::int32_t StringCmp(const String& a, const String& b)
	{
		std::int32_t cmp = 0;
		if (a < b) cmp = -1;
		else if (a > b) cmp = 1;
		return cmp;
	}

	// This function returns the index of the first position where the substring
	// exists in the input string. If the substring doesn't exist in the input
	// string -1 is returned.
	//
	// int string::findFirst(const string &in sub, uint start = 0) const
	static std::int32_t StringFindFirst(const String& sub, std::uint32_t start, const String& str)
	{
		auto found = str.exceptPrefix(start).find(sub);
		return (found != nullptr ? (std::int32_t)(found.begin() - str.begin()) : -1);
	}

	// This function returns the index of the first position where the one of the bytes in substring
	// exists in the input string. If the characters in the substring doesn't exist in the input
	// string -1 is returned.
	//
	// int string::findFirstOf(const string &in sub, uint start = 0) const
	static std::int32_t StringFindFirstOf(const String& sub, std::uint32_t start, const String& str)
	{
		auto found = str.exceptPrefix(start).findAny(sub);
		return (found != nullptr ? (std::int32_t)(found.begin() - str.begin()) : -1);
	}

	// This function returns the index of the last position where the one of the bytes in substring
	// exists in the input string. If the characters in the substring doesn't exist in the input
	// string -1 is returned.
	//
	// int string::findLastOf(const string &in sub, uint start = 0) const
	static std::int32_t StringFindLastOf(const String& sub, std::uint32_t start, const String& str)
	{
		auto found = str.exceptSuffix(start).findLastAny(sub);
		return (found != nullptr ? (std::int32_t)(found.begin() - str.begin()) : -1);
	}

	/*
	// This function returns the index of the first position where a byte other than those in substring
	// exists in the input string. If none is found -1 is returned.
	//
	// int string::findFirstNotOf(const string &in sub, uint start = 0) const
	static int StringFindFirstNotOf(const String& sub, std::uint32_t start, const String& str)
	{
		// We don't register the method directly because the argument types change between 32-bit and 64-bit platforms
		return (int)str.find_first_not_of(sub, (size_t)(start < 0 ? string::npos : start));
	}

	// This function returns the index of the last position where a byte other than those in substring
	// exists in the input string. If none is found -1 is returned.
	//
	// int string::findLastNotOf(const string &in sub, uint start = -1) const
	static int StringFindLastNotOf(const String& sub, std::uint32_t start, const String& str)
	{
		// We don't register the method directly because the argument types change between 32-bit and 64-bit platforms
		return (int)str.find_last_not_of(sub, (size_t)(start < 0 ? string::npos : start));
	}*/

	// This function returns the index of the last position where the substring
	// exists in the input string. If the substring doesn't exist in the input
	// string -1 is returned.
	//
	// int string::findLast(const string &in sub, int start = 0) const
	static std::int32_t StringFindLast(const String& sub, std::int32_t start, const String& str)
	{
		auto found = str.exceptSuffix(start).findLast(sub);
		return (found != nullptr ? (std::int32_t)(found.begin() - str.begin()) : -1);
	}

	/*
	// void string::insert(uint pos, const string &in other)
	static void StringInsert(unsigned int pos, const String& other, String& str)
	{
		// We don't register the method directly because the argument types change between 32-bit and 64-bit platforms
		str.insert(pos, other);
	}

	// void string::erase(uint pos, int count = -1)
	static void StringErase(unsigned int pos, int count, String& str)
	{
		// We don't register the method directly because the argument types change between 32-bit and 64-bit platforms
		str.erase(pos, (size_t)(count < 0 ? string::npos : count));
	}*/

	// uint string::size() const
	static std::uint32_t StringSize(const String& str)
	{
		// We don't register the method directly because the return type changes between 32-bit and 64-bit platforms
		return (std::uint32_t)str.size();
	}

	// void string::resize(uint l)
	/*static void StringResize(std::uint32_t l, String& str)
	{
		// We don't register the method directly because the argument types change between 32bit and 64bit platforms
		str.resize(l);
	}*/

	// string formatInt(int64 val, const string &in options, uint width)
	static String formatInt(std::int64_t value, const String& options, std::uint32_t width)
	{
		bool leftJustify = options.find("l") != nullptr;
		bool padWithZero = options.find("0") != nullptr;
		bool alwaysSign = options.find("+") != nullptr;
		bool spaceOnSign = options.find(" ") != nullptr;
		bool hexSmall = options.find("h") != nullptr;
		bool hexLarge = options.find("H") != nullptr;

		String fmt = "%";
		if (leftJustify) fmt += "-";
		if (alwaysSign) fmt += "+";
		if (spaceOnSign) fmt += " ";
		if (padWithZero) fmt += "0";

#if defined(DEATH_TARGET_WINDOWS)
		fmt += "*I64";
#elif defined(_LP64)
		fmt += "*l";
#else
		fmt += "*ll";
#endif

		if (hexSmall) fmt += "x";
		else if (hexLarge) fmt += "X";
		else fmt += "d";

		char buffer[64];
#if defined(DEATH_TARGET_MSVC)
		// MSVC 8.0 / 2005 or newer
		sprintf_s(buffer, sizeof(buffer), fmt.data(), width, value);
#else
		sprintf(buffer, fmt.data(), width, value);
#endif
		return buffer;
	}

	// string formatUInt(uint64 val, const string &in options, uint width)
	static String formatUInt(std::uint64_t value, const String& options, std::uint32_t width)
	{
		bool leftJustify = options.find('l') != nullptr;
		bool padWithZero = options.find('0') != nullptr;
		bool alwaysSign = options.find('+') != nullptr;
		bool spaceOnSign = options.find(' ') != nullptr;
		bool hexSmall = options.find('h') != nullptr;
		bool hexLarge = options.find('H') != nullptr;

		String fmt = "%";
		if (leftJustify) fmt += "-";
		if (alwaysSign) fmt += "+";
		if (spaceOnSign) fmt += " ";
		if (padWithZero) fmt += "0";

#if defined(DEATH_TARGET_WINDOWS)
		fmt += "*I64";
#elif defined(_LP64)
		fmt += "*l";
#else
		fmt += "*ll";
#endif

		if (hexSmall) fmt += "x";
		else if (hexLarge) fmt += "X";
		else fmt += "u";

		char buffer[64];
#if defined(DEATH_TARGET_MSVC)
		// MSVC 8.0 / 2005 or newer
		sprintf_s(buffer, sizeof(buffer), fmt.data(), width, value);
#else
		sprintf(buffer, fmt.data(), width, value);
#endif
		return buffer;
	}

	// string formatFloat(double val, const string &in options, uint width, uint precision)
	static String formatFloat(double value, const String& options, std::uint32_t width, std::uint32_t precision)
	{
		bool leftJustify = options.find("l") != nullptr;
		bool padWithZero = options.find("0") != nullptr;
		bool alwaysSign = options.find("+") != nullptr;
		bool spaceOnSign = options.find(" ") != nullptr;
		bool expSmall = options.find("e") != nullptr;
		bool expLarge = options.find("E") != nullptr;

		String fmt = "%";
		if (leftJustify) fmt += "-";
		if (alwaysSign) fmt += "+";
		if (spaceOnSign) fmt += " ";
		if (padWithZero) fmt += "0";

		fmt += "*.*";

		if (expSmall) fmt += "e";
		else if (expLarge) fmt += "E";
		else fmt += "f";

		char buffer[64];
#if defined(DEATH_TARGET_MSVC)
		// MSVC 8.0 / 2005 or newer
		sprintf_s(buffer, sizeof(buffer), fmt.data(), width, precision, value);
#else
		sprintf(buffer, fmt.data(), width, precision, value);
#endif
		return buffer;
	}

	// int64 parseInt(const string &in val, uint base = 10, uint &out byteCount = 0)
	static std::int64_t parseInt(const String& val, std::uint32_t base, std::uint32_t* byteCount)
	{
		if (base != 10 && base != 16) {
			if (byteCount) *byteCount = 0;
			return 0;
		}

		const char* end = val.data();

		bool sign = false;
		if (*end == '-') {
			sign = true;
			end++;
		} else if (*end == '+') {
			end++;
		}

		std::int64_t res = 0;
		if (base == 10) {
			while (*end >= '0' && *end <= '9') {
				res *= 10;
				res += *end++ - '0';
			}
		} else if (base == 16) {
			while ((*end >= '0' && *end <= '9') ||
					(*end >= 'a' && *end <= 'f') ||
					(*end >= 'A' && *end <= 'F')) {
				res *= 16;
				if (*end >= '0' && *end <= '9') {
					res += *end++ - '0';
				} else if (*end >= 'a' && *end <= 'f') {
					res += *end++ - 'a' + 10;
				} else if (*end >= 'A' && *end <= 'F') {
					res += *end++ - 'A' + 10;
				}
			}
		}

		if (byteCount) {
			*byteCount = std::uint32_t(size_t(end - val.data()));
		}
		if (sign) {
			res = -res;
		}
		return res;
	}

	// uint64 parseUInt(const string &in val, uint base = 10, uint &out byteCount = 0)
	static std::uint64_t parseUInt(const String& val, std::uint32_t base, std::uint32_t* byteCount)
	{
		if (base != 10 && base != 16) {
			if (byteCount) *byteCount = 0;
			return 0;
		}

		const char* end = val.data();

		std::uint64_t res = 0;
		if (base == 10) {
			while (*end >= '0' && *end <= '9') {
				res *= 10;
				res += *end++ - '0';
			}
		} else if (base == 16) {
			while ((*end >= '0' && *end <= '9') ||
				(*end >= 'a' && *end <= 'f') ||
				(*end >= 'A' && *end <= 'F')) {
				res *= 16;
				if (*end >= '0' && *end <= '9') {
					res += *end++ - '0';
				} else if (*end >= 'a' && *end <= 'f') {
					res += *end++ - 'a' + 10;
				} else if (*end >= 'A' && *end <= 'F') {
					res += *end++ - 'A' + 10;
				}
			}
		}

		if (byteCount) {
			*byteCount = std::uint32_t(size_t(end - val.data()));
		}
		return res;
	}

	// double parseFloat(const string &in val, uint &out byteCount = 0)
	double parseFloat(const String& val, std::uint32_t* byteCount)
	{
		char* end;

		// WinCE doesn't have setlocale. Some quick testing on my current platform still manages to parse the numbers
		// such as "3.14" even if the decimal for the locale is ",".
#if !defined(DEATH_TARGET_ANDROID) && !defined(_WIN32_WCE) && !defined(__psp2__)
		// Set the locale to C so that we are guaranteed to parse the float value correctly
		char* tmp = setlocale(LC_NUMERIC, nullptr);
		String orig = (tmp ? tmp : "C"_s);
		setlocale(LC_NUMERIC, "C");
#endif

		double res = strtod(val.data(), &end);

#if !defined(DEATH_TARGET_ANDROID) && !defined(_WIN32_WCE) && !defined(__psp2__)
		// Restore the locale
		setlocale(LC_NUMERIC, orig.data());
#endif

		if (byteCount) {
			*byteCount = std::uint32_t(size_t(end - val.data()));
		}
		return res;
	}

	// This function returns a string containing the substring of the input string
	// determined by the starting index and count of characters.
	//
	// string string::substr(uint start = 0, int count = -1) const
	static String StringSubString(std::uint32_t start, std::int32_t count, const String& str)
	{
		String ret;
		if (start < str.size() && count != 0) {
			ret = str.slice(start, (size_t)(count < 0 ? str.size() : start + count));
		}
		return ret;
	}

	// String equality comparison.
	// Returns true iff lhs is equal to rhs.
	//
	// For some reason gcc 4.7 has difficulties resolving the asFUNCTIONPR(operator==, (const string &, const string &) macro, so this wrapper was introduced as work around.
	static bool StringEquals(const String& lhs, const String& rhs)
	{
		return lhs == rhs;
	}

	static String StringConcat(const String& lhs, const String& rhs)
	{
		return lhs + rhs;
	}

	void RegisterString(asIScriptEngine* engine)
	{
		std::int32_t r;

		// Register the string type
		r = engine->RegisterObjectType("string", sizeof(String), asOBJ_VALUE | asGetTypeTraits<String>()); RETURN_ASSERT(r >= 0);

		r = engine->RegisterStringFactory("string", GetStringFactoryInstance());

		// Register the object operator overloads
		r = engine->RegisterObjectBehaviour("string", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructString), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
		r = engine->RegisterObjectBehaviour("string", asBEHAVE_CONSTRUCT, "void f(const string &in)", asFUNCTION(CopyConstructString), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
		r = engine->RegisterObjectBehaviour("string", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(DestructString), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
		r = engine->RegisterObjectMethod("string", "string &opAssign(const string &in)", asMETHODPR(String, operator=, (const String&), String&), asCALL_THISCALL); RETURN_ASSERT(r >= 0);
		// Need to use a wrapper on Mac OS X 10.7/XCode 4.3 and CLang/LLVM, otherwise the linker fails
		r = engine->RegisterObjectMethod("string", "string &opAddAssign(const string &in)", asFUNCTION(AddAssignStringToString), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
		//r = engine->RegisterObjectMethod("string", "string &opAddAssign(const string &in)", asMETHODPR(string, operator+=, (const string&), string&), asCALL_THISCALL); RETURN_ASSERT( r >= 0 );

		// Need to use a wrapper for operator== otherwise gcc 4.7 fails to compile
		r = engine->RegisterObjectMethod("string", "bool opEquals(const string &in) const", asFUNCTIONPR(StringEquals, (const String&, const String&), bool), asCALL_CDECL_OBJFIRST); RETURN_ASSERT(r >= 0);
		r = engine->RegisterObjectMethod("string", "int opCmp(const string &in) const", asFUNCTION(StringCmp), asCALL_CDECL_OBJFIRST); RETURN_ASSERT(r >= 0);
		r = engine->RegisterObjectMethod("string", "string opAdd(const string &in) const", asFUNCTION(StringConcat), asCALL_CDECL_OBJFIRST); RETURN_ASSERT(r >= 0);

		// The string length can be accessed through methods or through virtual property
#if AS_USE_ACCESSORS != 1
		r = engine->RegisterObjectMethod("string", "uint length() const", asFUNCTION(StringSize), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
#endif
		//r = engine->RegisterObjectMethod("string", "void resize(uint)", asFUNCTION(StringResize), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
#if AS_USE_STLNAMES != 1 && AS_USE_ACCESSORS == 1
		// Don't register these if STL names is used, as they conflict with the method size()
		r = engine->RegisterObjectMethod("string", "uint get_length() const property", asFUNCTION(StringSize), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
		//r = engine->RegisterObjectMethod("string", "void set_length(uint) property", asFUNCTION(StringResize), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
#endif
		// Need to use a wrapper on Mac OS X 10.7/XCode 4.3 and CLang/LLVM, otherwise the linker fails
		//r = engine->RegisterObjectMethod("string", "bool empty() const", asMETHOD(string, empty), asCALL_THISCALL); RETURN_ASSERT( r >= 0 );
		r = engine->RegisterObjectMethod("string", "bool empty() const", asFUNCTION(StringIsEmpty), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);

		// Register the index operator, both as a mutator and as an inspector
		// Note that we don't register the operator[] directly, as it doesn't do bounds checking
		r = engine->RegisterObjectMethod("string", "uint8 &opIndex(uint)", asFUNCTION(StringCharAt), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
		r = engine->RegisterObjectMethod("string", "const uint8 &opIndex(uint) const", asFUNCTION(StringCharAt), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);

		// Automatic conversion from values
		r = engine->RegisterObjectMethod("string", "string &opAssign(double)", asFUNCTION(AssignDoubleToString), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
		r = engine->RegisterObjectMethod("string", "string &opAddAssign(double)", asFUNCTION(AddAssignDoubleToString), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
		r = engine->RegisterObjectMethod("string", "string opAdd(double) const", asFUNCTION(AddStringDouble), asCALL_CDECL_OBJFIRST); RETURN_ASSERT(r >= 0);
		r = engine->RegisterObjectMethod("string", "string opAdd_r(double) const", asFUNCTION(AddDoubleString), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);

		r = engine->RegisterObjectMethod("string", "string &opAssign(float)", asFUNCTION(AssignFloatToString), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
		r = engine->RegisterObjectMethod("string", "string &opAddAssign(float)", asFUNCTION(AddAssignFloatToString), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
		r = engine->RegisterObjectMethod("string", "string opAdd(float) const", asFUNCTION(AddStringFloat), asCALL_CDECL_OBJFIRST); RETURN_ASSERT(r >= 0);
		r = engine->RegisterObjectMethod("string", "string opAdd_r(float) const", asFUNCTION(AddFloatString), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);

		r = engine->RegisterObjectMethod("string", "string &opAssign(int64)", asFUNCTION(AssignInt64ToString), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
		r = engine->RegisterObjectMethod("string", "string &opAddAssign(int64)", asFUNCTION(AddAssignInt64ToString), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
		r = engine->RegisterObjectMethod("string", "string opAdd(int64) const", asFUNCTION(AddStringInt64), asCALL_CDECL_OBJFIRST); RETURN_ASSERT(r >= 0);
		r = engine->RegisterObjectMethod("string", "string opAdd_r(int64) const", asFUNCTION(AddInt64String), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);

		r = engine->RegisterObjectMethod("string", "string &opAssign(uint64)", asFUNCTION(AssignUInt64ToString), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
		r = engine->RegisterObjectMethod("string", "string &opAddAssign(uint64)", asFUNCTION(AddAssignUInt64ToString), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
		r = engine->RegisterObjectMethod("string", "string opAdd(uint64) const", asFUNCTION(AddStringUInt64), asCALL_CDECL_OBJFIRST); RETURN_ASSERT(r >= 0);
		r = engine->RegisterObjectMethod("string", "string opAdd_r(uint64) const", asFUNCTION(AddUInt64String), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);

		r = engine->RegisterObjectMethod("string", "string &opAssign(bool)", asFUNCTION(AssignBoolToString), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
		r = engine->RegisterObjectMethod("string", "string &opAddAssign(bool)", asFUNCTION(AddAssignBoolToString), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
		r = engine->RegisterObjectMethod("string", "string opAdd(bool) const", asFUNCTION(AddStringBool), asCALL_CDECL_OBJFIRST); RETURN_ASSERT(r >= 0);
		r = engine->RegisterObjectMethod("string", "string opAdd_r(bool) const", asFUNCTION(AddBoolString), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);

		// Utilities
		r = engine->RegisterObjectMethod("string", "string substr(uint start = 0, int count = -1) const", asFUNCTION(StringSubString), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
		r = engine->RegisterObjectMethod("string", "int findFirst(const string &in, uint start = 0) const", asFUNCTION(StringFindFirst), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
		r = engine->RegisterObjectMethod("string", "int findFirstOf(const string &in, uint start = 0) const", asFUNCTION(StringFindFirstOf), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
		//r = engine->RegisterObjectMethod("string", "int findFirstNotOf(const string &in, uint start = 0) const", asFUNCTION(StringFindFirstNotOf), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
		r = engine->RegisterObjectMethod("string", "int findLast(const string &in, int start = -1) const", asFUNCTION(StringFindLast), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
		r = engine->RegisterObjectMethod("string", "int findLastOf(const string &in, int start = -1) const", asFUNCTION(StringFindLastOf), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
		//r = engine->RegisterObjectMethod("string", "int findLastNotOf(const string &in, int start = -1) const", asFUNCTION(StringFindLastNotOf), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
		//r = engine->RegisterObjectMethod("string", "void insert(uint pos, const string &in other)", asFUNCTION(StringInsert), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);
		//r = engine->RegisterObjectMethod("string", "void erase(uint pos, int count = -1)", asFUNCTION(StringErase), asCALL_CDECL_OBJLAST); RETURN_ASSERT(r >= 0);

		r = engine->RegisterGlobalFunction("string formatInt(int64 val, const string &in options = \"\", uint width = 0)", asFUNCTION(formatInt), asCALL_CDECL); RETURN_ASSERT(r >= 0);
		r = engine->RegisterGlobalFunction("string formatUInt(uint64 val, const string &in options = \"\", uint width = 0)", asFUNCTION(formatUInt), asCALL_CDECL); RETURN_ASSERT(r >= 0);
		r = engine->RegisterGlobalFunction("string formatFloat(double val, const string &in options = \"\", uint width = 0, uint precision = 0)", asFUNCTION(formatFloat), asCALL_CDECL); RETURN_ASSERT(r >= 0);
		r = engine->RegisterGlobalFunction("int64 parseInt(const string &in, uint base = 10, uint &out byteCount = 0)", asFUNCTION(parseInt), asCALL_CDECL); RETURN_ASSERT(r >= 0);
		r = engine->RegisterGlobalFunction("uint64 parseUInt(const string &in, uint base = 10, uint &out byteCount = 0)", asFUNCTION(parseUInt), asCALL_CDECL); RETURN_ASSERT(r >= 0);
		r = engine->RegisterGlobalFunction("double parseFloat(const string &in, uint &out byteCount = 0)", asFUNCTION(parseFloat), asCALL_CDECL); RETURN_ASSERT(r >= 0);
	}
}

#endif