- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
const char *GetExternalFilesDir ()
{
assert (started);
static std::string s="";
if (s.length()==0)
{
LOGI("Try get external files dir");
jclass c; jmethodID m; jobject o;
c = env->FindClass ("android/app/NativeActivity");
m = env->GetMethodID (c, "getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;");
o = env->CallObjectMethod (appState->activity->clazz, m, NULL);
c = env->GetObjectClass (o);
m = env->GetMethodID (c, "getAbsolutePath", "()Ljava/lang/String;");
o = env->CallObjectMethod (o, m);
jstring jo = (jstring)o;
const char *path = env->GetStringUTFChars(jo, NULL);
s=path;
env->ReleaseStringUTFChars(jo, path);
s+='/';
LOGI("Path for program's data files is %s", s.c_str());
}
return s.c_str();
}
Этот код был написан одним очень известным в очень узких кругах человеком. Будем надеяться, что он придет в обсуждение и прокомментирует его.
В const char *.
Ну и не потокобезопасно, но на это, скорее всего, пофиг.
Да ничего особо страшного. Просто потом автор к нему по-любому будет конкатенировать имена файлов. А с джвумя const char * (второй - литерал с именем файла) это делать малость противно.
А со статиком UB'а нет*. Да и переменная только для чтения, поэтому бояться за буфер, возвращаемый c_str не придётся.
* если джва потока не войдут в GetExternalFilesDir() в первый раз одновременно
переменную только для чтения можно познакомить с const_cast. Надо копию возвращать. Полюбэ.
А чем она поможет этому коду?
s атомарно и ровно один раз проинициализируется пустой строкой, а дальше оба потока войдут в if...
Синхронизировать с чем? Видимо с чтением этой переменной в строках 5 и 25. Т.е. всю функцию защитить мутексом.
Поэтому мутекс надо брать перед вызовом функции и не отпускать до окончания чтения кишков.
Проще сделать s thread-local.
Ну либо понадеяться на единственность вызова инициализатора, и тупо вынести всё получение строки в отдельную функцию. Как-то так, например:
Про многопоточную среду c++98 не слышал и никаких гарантий тут дать не может.
Само собой.
Но ведь почти все реализации стандарта знают о потоках. Так что можно надеяться хотя бы на implementation defined на конкретных компиляторах. Вот поэтому и спрашиваю ;)
P.S. Забавы ради попробовал вызвать функцию из инициализатора статика, лежащего в ней же... terminate called after throwing an instance of '__gnu_cxx::recursive_init_error'.
А учитывая то, что возвращается const char *, с которым могут сделать всё, что угодно - мутекс придется взять раз и навсегда. Ну либо отпускать вручную с вызывающей стороны, что тоже не айс...
Не забывай, на сотиках стоят ARM'ы с достаточно мягкой моделью памяти. С двухъядерным ARM'ом "авось проканает" не проканывает, в отличие от интелей.
Если эта функция должна дёргаться из плюсового кода - нужно возвращать std::string.
Если из жабы - тип возврата должен быть jstring, нужно аллоцировать строку через env, пусть жаба её сама освобождает.
+1
Иначе получается жопа, из-за которой функцию уже никогда не переделать в возвращающую разные значения и, при этом, реентерабельную. Хотя, в случае данного кода, на это можно забить болт - каталог во время работы не меняется.
P.S. Ну либо по канонам winapi возложить ответственность за выделение памяти на пользователя функции. Но ему это не понравится.
А, в сишке же нету const char*, да? Тогда не знаю.
Есть.
Если он не меняется в течение работы программы. То есть функция ведёт себя так, будто возвращает указатель на какую-то константу.
Ну не особо ок, если функция может возвращать разные значения. Из-за подобной питушни со strtok жопы у разрабов стандартной либы пригорели, когда появились потоки.
> Если он не меняется в течение работы программы.
Для таких случаев - ок. Если не забыть написать в доке, что не надо вызывать free() на результате этой функции.
> ведёт себя так, будто возвращает указатель на какую-то константу
Но ведь она на самом деле возвращает указатель на какую-то константу...
Есть. Я лично периодически использую const для индикации владения. Если функция возвращает неконстантный указатель, она как-бы-говорит-нам, что содержимое полностью принадлежит нам, мы можем туда гадить и отвечаем за его удаление.
> с которым ничего не случится
Пока не появится второй поток? Кмк, проще быстренько аллоцировать строку, чем постоянно платить за синхронизацию.
И слазить в жабу по JNI, не забываем.
Хотя для файлов - насрать. Копейки по сравнению с парсингом картинок\моделек\конфигов и самим чтением с флеша, а тем более HDD.
Можно на старте на уровне приложения закешировать. Положить всё нужное в какую-нибудь глобальную структурку Game и юзать всюду без синхронизации и лишних аллокаций.
Вот кстати да. Поэтому я и не люблю ленивые синглтоны - с ними ёбли больше, чем профита.
So true...
+1
Их даже в жабах с многопоточными примитивами и моделью памяти из коробки, не сильно тривиально написать, а в крестах старого стандарта и вовсе можно повеситься.
Ах да, у ведра в случае исключения прога тупо виснет.
Вот кстати да.
Но в целом вопрос спорный. "постоянно платить" - это не факт. С одной стороны синхронизация без contentionа может оказаться почти бесплатной (по цене CAS), благодаря всяким хитрым lock elision.
C другой стороны хватать мьютекс явно дороже чем алоцировать строчку.
Но ведь аллоцирование строчки тоже содержит в себе атомарные операции...
Вообще в ОС обычно есть разные типы мьютексов, легковесные работают очень быстро, при частом однопоточном доступе, но тупят когда сильная конкуренция, и наоборот.
На старте может так случиться что джва и более раз запросим у JNI.
Ну так я о нем выше написал ;)
А лочку можно прикрутить уже после directory.load(). И на всех проходах кроме первого она не скажется.
>И на всех проходах кроме первого она не скажется.
Что-то это мне напоминает... А точно! Это ведь похоже на ленивый синглтон!
Опять возвращаемся к:
Поэтому я и не люблю ленивые синглтоны - с ними ёбли больше, чем профита.
По сути это такой ленивый Future.
Только эти новые фишки всё-равно Тарасу не подойдут, он же пишет под старый стандарт.
А ты как думал? Просто профит в том что это реализовали 1 раз и правильно.
Вообще полагаю там double checked locking, типа как у борманда, только с мьютексом.
1. атомарная проверка
2. мьютекс
3. выполнение метода.
4. освободить мьютекс
То есть мьютекс предотвращает множественные вызовы на инициализации, но когда пройдет первая инициализация, то до лочки код доходить уже не будет.
Плюс сами мьютексы обычно тоже сделаны на атомиках, при отсутствии конкуренции и waitов.
Может. Но если она мютексе, то, скорее всего, на этой архитектуре по-другому его уже никак не запилить. Или авторы компилятора - ленивые обезьяны. Имхо.
http://www.gamedev.ru/projects/forum/?id=193863&page=9#m128
Ещё он говорит что я должен использовать указатели, иначе зачем мне С++.
Алсо поздравьте с первым местом
http://www.gamedev.ru/flame/forum/?id=193145
>Ещё он говорит что я не должен использовать указатели, иначе мне С++.
>Алсо поздравьте с первым местом
Малаца. А хуле, народ не проведешь...
Хотя я кроме Хулиона не видел игр других участников, но игрушка - зачётный олдскул.
Другие игры можно по видео прикинуть: http://www.gamedev.ru/flame/forum/?id=135442&page=9#m132
А оно когда генерирует уровень, как проверяет что его можно пройти?
Ну что ключи и двери будут открываться по мере прохождения?
PS> Мне понравилось когда стреляешь, как мясо разлетается...
Я собираюсь статью написать, но мне пока влом.
Ждите обновления в теме проекта.
// поиск1: в сторону пустой ячейки
// поиск2: вылезти в место, где были ранее
// злоупотреблять не стоит, а то одни двери везде будут, лол
// поиск3: поход против шерсти
// поиск4: куда попало
http://www.gamedev.ru/files/?id=101336
И комментарии экспертов
Ты не тот язык программирования выбрал, тебе надо писать на C#.
Какая-то странная помесь C-стиля, STL и BOOST. Сквозь весь проект висит std::stringstream, в котором объявлены в том числе и std::string, но многие функции принимают const char *. Указатели почти нигде не используются, управления памятью тоже нет. Есть классы, но рядом мы видим глобальные функции, принимающие указатель на структуру.
В общем, стиля нет. Либо ты каждый кусок кода рождаешь путём экспериментов со средствами языка, либо разные части кода писали разные люди, либо разные части кода написаны в разом психическом состоянии...
Указатели почти нигде не используются, управления памятью тоже нет.
Есть классы, но рядом мы видим глобальные функции, принимающие указатель на структуру.
Он так говорит будто указатели и ручное управление памятью - что-то хорошее.
Но стиль действительно необычный. Если любовь к struct я понять еще могу (сам часто пишу бубличные поля), но вот почему весь код в h-файлах?
Именно это он и имеет в виду. А ты думал, что школоло-кулхацкеры - это я преувеличивал?
> но вот почему весь код в h-файлах
ну меня с дельфей заебало писать заголовки методов по два раза, думать, что выносить наружу, что не выносить
и первый мой опыт на крестах обломал меня с шаблонами в cpp и с глобальными переменными, поэтому я cpp недолюблюваю
Бустянка.
Плюс Тарас же ненавидит буст, и что главное в Аде такое разделение практикуется повсеместно.
http://www.adahome.com/ammo/cpp2ada.html#2
Any Ada package on the other hand consists of two parts, the specification (header) and body (code). The specification however is a completely stand alone entity which can be compiled on its own and so must include specifications from other packages to do so. An Ada package body at compile time must refer to its package specification to ensure legal declarations, but in many Ada environments it would look up a compiled version of the specification.
Имхо, писать побольше всякого кода (желательно шаблонных рекурсивных compile-time 'оптимизаций') в h-файлы - лучший способ замедлить компиляцию юзерам этих самых h-файлов.
Это дизеринг? А чо свищ, а не массив, например?
http://www.gamedev.ru/projects/forum/?id=193863&page=10#m143
Эх. Завлекает...
Не ведитесь, там нет ничего о генераторе бреда, только гейдев.