While Alchemy supports compiling C++, it seems that using the using the STL is trouble, mostly due to a problem with std::string. What's strange is that Alchemy seems to be using GNU libstd++ v3.4.6. It's hard to believe that std::string is broken in GNU's STL.
Has anyone figured out any workarounds for this problem? C++ without the STL is like a fish without water.
The problem isn't in the STL as such. The GNU implementation of std::string
is reference-counted using the thread-safe functions __gnu_cxx::__exchange_and_add
and __gnu_cxx::__atomic_add
. The problem is that __exchange_and_add
/__atomic_add
are broken.
The solution is to rebuild the STL with good implementations of these functions.
Fortunately, the Alchemy distro leaves some breadcrumbs for us. See $ALCHEMY_HOME/avm2-libc/README
, which tells us how to do it:
The sources used to build avm2-libstdc++.l.bc can be downloaded here:
http://download.macromedia.com/pub/labs/alchemy/alchemy_gnucpp3-4library_121008.zip
To build avm2-libstdc++.l.bc:
cd $ALCHEMY_HOME/avm2-libc
unzip alchemy_gnucpp3-4library_121008.zip
mv lib/avm2-libstdc++.l.bc lib/avm2-libstdc++.l.bc.OLD
make
You should *not* run achacks prior to using these building this library with make.
The Makefiles provided have been preconfigured to use LLVM directly where needed.
Normally I'd expect to find the implementations of __exchange_and_add/__atomic_add in libstd++ ($ALCHEMY_HOME/avm2-libc/lib/avm2-libstdc++.l.bc
), but for some reason they're defined in libc ($ALCHEMY_HOME/avm2-libc/lib/avm2-libc.l.bc
).
I'm not sure why this is the case, but we can work around it by hacking atomicity.h, where the prototypes are kept. Note that after unpacking alchemy_gnucpp3-4library_121008.zip, you'll need to edit two atomicity.h files:
$ALCHEMY_HOME/avm2-libc/include/c++/3.4/bits/atomicity.h
$ALCHEMY_HOME/avm2-libc/libstdc++/include/bits/atomicity.h
Here is the code:
/*
* __exchange_and_add and __atomic_add are broken in Alchemy's libc.
* Replace them with functioning implementations. This isn't
* cross-platform, but this codebase is only compiling for Alchemy anyway.
*/
#define __exchange_and_add(x,y) __exchange_and_add_fix((x),(y))
#define __atomic_add(x,y) __exchange_and_add_fix((x),(y))
/*
* Correctly implement __exchange_and_add. It's not thread-safe,
* but Alchemy isn't threaded, so we should be ok.
*/
inline _Atomic_word __exchange_and_add_fix(volatile _Atomic_word* __mem, int __val) {
int orig= *__mem;
(*__mem)+= __val;
return orig;
}
Here is some test code to run to make sure the rebuilt STL works:
#include <cstdio>
#include <string>
#include <map>
#include <fstream>
using namespace std;
void string_test() {
string s1;
string s2;
s1 = "a";
s2 = s1; // copy constructor
s1 = "b";
// use your favorite TRACE function here
printf("s1= %s \n", s1.c_str()); // expected: "b", actual: "b"
printf("s2= %s \n", s2.c_str()); // expected: "a", actual: "b", ERROR
}
void map_test() {
map<string, int> test_map;
test_map["test1"]= 1;
test_map["test2"]= 2;
test_map["test3"]= 3;
string tmp= "test1";
printf("%s : %d \n", tmp.c_str(), test_map[tmp]);
}
void ifstream_test()
{
std::ifstream in("test.txt");
// ERROR 1:
// Trying to seek file throws an error:
// Error #1006: value is not a function.
// at: basic_filebuf::char_traits::seekoff
in.seekg(0, std::ios::end);
int length = in.tellg();
in.seekg(0, std::ios::beg);
printf("File Length: %d \n", length);
while(in.good()) {
char buffer[512];
// ERROR 2:
// RangeError: Error #1125: The index 1092156 is out of range 721.
// at basic_filebuf::char_traits::underflow::work()
in.getline(buffer, 512, '\n');
printf("buffer= %s \n", buffer);
}
}
int main() {
string_test();
map_test();
ifstream_test();
return 0;
}
Note that the rebuilt STL appears to fix some related issues with map and ifstream.