Re[4]: trick
От: B0FEE664  
Дата: 01.10.15 11:05
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

Вот такой вариант работает и для стандарта С++14, и в Visual Studio 2013:
#include <iostream>
#include <vector>

std::vector<const char*> g_Strings;

const char* AddToGlobalArray(const char* pStr)
{
  g_Strings.push_back(pStr);
  return pStr;
}


template <class T>
class StrMap
{
  public:
    static const char* const m_p;
};

//static
template <class T>
const char* const StrMap<T>::m_p = AddToGlobalArray(T().get());

void fun()
{
    struct GetStr{ const char* get() { return "asdf"; } };

    const char* p = StrMap<GetStr>::m_p;
}


void fun2()
{
    struct GetStr{ const char* get() { return "qwerty"; } };

    const char* p = StrMap<GetStr>::m_p;
}


int main(int argc, char* argv[])
{
    for(auto x : g_Strings)
        std::cout << x << std::endl;

    return 0;
}


Output:
asdf
qwerty
И каждый день — без права на ошибку...
Re[5]: trick
От: Evgeny.Panasyuk Россия  
Дата: 01.10.15 13:05
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Вот такой вариант работает и для стандарта С++14, и в Visual Studio 2013:


Да, здорово, вот только на Clang что-то не заводится.
Хотя два из трёх компиляторов это уже серьёзно.
Re[6]: trick
От: T4r4sB Россия  
Дата: 01.10.15 13:13
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:


EP>Да, здорово, вот только на Clang что-то не заводится.


Судя по выхлопу, strings очищается перед вызовом мейна?!
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[7]: trick
От: Evgeny.Panasyuk Россия  
Дата: 01.10.15 13:34
Оценка:
Здравствуйте, T4r4sB, Вы писали:

EP>>Да, здорово, вот только на Clang что-то не заводится.

TB>Судя по выхлопу, strings очищается перед вызовом мейна?!

Видимо не очищается, а просто конструируется позже run_it, хотя по идее должен раньше (можно попробовать сделать тест с порядком вызова конструкторов).
С синглтоном Майерса работает и на Clang.
Отредактировано 01.10.2015 13:35 Evgeny.Panasyuk . Предыдущая версия . Еще …
Отредактировано 01.10.2015 13:34 Evgeny.Panasyuk . Предыдущая версия .
Re[8]: trick
От: Evgeny.Panasyuk Россия  
Дата: 01.10.15 13:40
Оценка:
EP>Видимо не очищается, а просто конструируется позже run_it, хотя по идее должен раньше (можно попробовать сделать тест с порядком вызова конструкторов).

Да, именно так и происходит:
#include <cstdio>

using namespace std;

struct First
{
    First()
    {
        printf("first\n");
    }
} first;

template<class T>
T run_it = T{};

void not_used()
{
    struct Second
    {
        Second()
        {
            printf("second\n");
        }
    };
    Second t = run_it<Second>;
}

int main()
{
}

// GCC:
first
second

// Clang:
second
first
Re[7]: Чудны способности твои, о стандарт !
От: flаt  
Дата: 02.10.15 08:22
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>А вот, только что придумал:


BFE>template<class T>

BFE>T x = T{};

BFE> Test t = x<Test>;


Кто разъяснит, как это работает? Ведь до инициализации Test исполнение кода не доходит, почему же T<> инициализируется?
Какая связь между инстанцированием шаблона (T, StrMap) в локальном коде и инициализацией глобальной переменной?

Куда ткнуться почитать?
Re[8]: Чудны способности твои, о стандарт !
От: B0FEE664  
Дата: 02.10.15 09:42
Оценка: 4 (1)
Здравствуйте, flаt, Вы писали:

BFE>>А вот, только что придумал:

BFE>>template<class T>
BFE>>T x = T{};

BFE>> Test t = x<Test>;


F>Кто разъяснит, как это работает? Ведь до инициализации Test исполнение кода не доходит, почему же T<> инициализируется?

F>Какая связь между инстанцированием шаблона (T, StrMap) в локальном коде и инициализацией глобальной переменной?

x — это глобальная переменная, следовательно она должна быть инициализирована в глобальном пространстве. То, что определение класса лежит где-то ещё не столь важно.
Дело тут даже не столько в том, что введены шаблонные переменные, а в том, что теперь в качестве параметра шаблона можно указать локальный класс. В сообщении trick, выше, это видно.
С этим кодом, кстати, возможна проблема из-за порядка инициализации глобальных переменных. Для надёжности, x следует сделать константой: const T x = T{};

F>Куда ткнуться почитать?

Не знаю, я стандарт просматривал...
И каждый день — без права на ошибку...
Re: Чудны способности твои, о стандарт !
От: wander  
Дата: 02.10.15 11:11
Оценка: 14 (2)
Здравствуйте, B0FEE664, Вы писали:

BFE>Может уже появился какой способ собрать указатели на локальные строки в глобальных переменных ? (Например, шаблон параметризованный локальной строкой...)


Это и в С++11 можно было сделать.
Вот ты там писал, что это можно использовать для перевода приложения.
Понравилась твоя идея, вот мой концепт реализации (работает в GCC и СLang c С++11).
В принципе даже контейнеры не понадобились.
Вспомогательная кухня, применил тут кое-какие свои давние идеи по реализации настоящих ct-строк:
  ct_string.h
#ifndef CT_STRING_H_INCLUDED
#define CT_STRING_H_INCLUDED

namespace ct
{

template <char ...Chars>
struct string
{
    enum { length = sizeof...(Chars) - 1 };

    static char const value[];
};

template <char ...Chars>
char const string<Chars...>::value[] = { Chars... };

} // ct

#endif // CT_STRING_H_INCLUDED

  ct_indices_utils.h
#ifndef CT_INDICES_UTILS_H_INCLUDED
#define CT_INDICES_UTILS_H_INCLUDED

#include <cstddef>

namespace ct
{

template <size_t ...I>
struct indices
{ };

template <size_t Max, size_t ...Indices>
struct make_indices
    : make_indices<Max - 1, Max - 1, Indices...>
{ };

template <size_t ...Indices>
struct make_indices<0, Indices...>
    : indices<Indices...>
{
    using type = indices<Indices...>;
};

} // ct

#endif // CT_INDICES_UTILS_H_INCLUDED

  ct_string_utils.h
#ifndef CT_STRING_UTILS_H_INCLUDED
#define CT_STRING_UTILS_H_INCLUDED

#include "ct_string.h"
#include "ct_indices_utils.h"

namespace ct
{

template <typename Str, typename I>
struct string_gen;

template <typename Str, size_t ...I>
struct string_gen<Str, ::ct::indices<I...>>
    : ::ct::string<Str{}.chars[I]...>
{ };

template <typename Str, size_t Len>
struct make_string
    : string_gen<Str, typename ::ct::make_indices<Len>::type>
{ };

} // ct

#endif // CT_STRING_UTILS_H_INCLUDED

Сам транслятор, довольно примитивный.
  translator.h
#ifndef TRANSLATOR_H_INCLUDED
#define TRANSLATOR_H_INCLUDED

#include "ct_string.h"
#include "ct_indices_utils.h"
#include "ct_string_utils.h"

class Translator
{
public:
    enum { LangMax = 5 };

    struct Holder
    {
        char const * m_strings[LangMax];
    };

private:
    template <typename Value>
    struct Storage : Value, Holder
    {
        template <size_t ...I>
        Storage(Translator & self, ct::indices<I...>)
            : Holder{ { ((void)I, Value::value)... } }
        { }

        explicit Storage(Translator & self)
            : Storage(self, ct::make_indices<LangMax> {})
        { }
    };

    template <char ...Chars>
    Holder * addString(ct::string<Chars...> const &)
    {
        static Storage<ct::string<Chars...>> s(*this);
        return &s;
    }

public:
    static Translator & instance()
    {
        static Translator inst;
        return inst;
    }

    template <typename Original, typename Translated>
    void addTranslation(size_t lang)
    {
        Holder * h = this->addString(Original{});
        h->m_strings[lang] = Translated::value;
    }
    template <typename Original>
    char const * addString()
    {
        Holder * h = this->addString(Original{});
        return h->m_strings[m_lang];
    }

    static void setLang(size_t lang)
    {
        instance().m_lang = lang;
    }

private:
    size_t m_lang = 0;
};

#define TR(str) []() \
    {                                                                   \
        struct StringType1 { const char * chars = (str); };             \
        using type1 = ::ct::make_string<StringType1, sizeof((str))>;    \
        return Translator::instance().addString<type1>();               \
    }()

#define SETUP_TR(lang, orig, trans) []() \
    {                                                                   \
        struct StringType1 { const char * chars = (orig);  };           \
        using type1 = ::ct::make_string<StringType1, sizeof((orig))>;   \
        struct StringType2 { const char * chars = (trans); };           \
        using type2 = ::ct::make_string<StringType2, sizeof((trans))>;  \
        Translator::instance().addTranslation<type1, type2>(lang);      \
        return true;                                                    \
    }()

#endif // TRANSLATOR_H_INCLUDED

Использование:
#include <cstdio>

#include "translator.h"

enum { RUS = 1 };

namespace {
bool f1 = SETUP_TR(RUS, "start", "старт");
bool f2 = SETUP_TR(RUS, "text", "текст");
}

int main()
{
    Translator::setLang(RUS);

    printf("%s\n", TR("start"));
    printf("%s\n", TR("start"));
    printf("%s\n", TR("text"));
}

SETUP_TR могут находиться в любом файле. Допустим, можно завести russian_tr.cpp и там написать перевод для нужных строк.
В продвинутой реализации можно было бы уйти от голых указателей и сделать свой класс "переводной" строки.
Тест "одним файлом" в онлайн компиляторе: http://rextester.com/WYUMS67445
Может быть будет полезно.
Отредактировано 02.10.2015 11:14 wander (Опечатки) . Предыдущая версия .
Re[2]: Чудны способности твои, о стандарт !
От: Evgeny.Panasyuk Россия  
Дата: 02.10.15 11:37
Оценка: 6 (1)
Здравствуйте, wander, Вы писали:

W>Вспомогательная кухня, применил тут кое-какие свои давние идеи по реализации настоящих ct-строк:


Хм, а насколько вот такой код соответствует стандарту?
int main()
{
    struct X
    {
        const char *chars = "abc";
    };
    static_assert(X{}.chars[0] == 'a', "");
    constexpr char x = X{}.chars[0];
}

Clang и GCC компилируют. Но разве X{}.chars[0] в этом случае constexpr?

Я в подобном случае
Автор: Evgeny.Panasyuk
Дата: 12.10.14
использовал constexpr метод:
#define CTTE_WRAP_STRING(x) \
    [] \
    { \
        struct { static constexpr auto value() { return x;} } str; \
        return str; \
    } \
/**/
Re[3]: Чудны способности твои, о стандарт !
От: Evgeny.Panasyuk Россия  
Дата: 02.10.15 11:52
Оценка:
EP>
EP>    struct X
EP>    {
EP>        const char *chars = "abc";
EP>    };
EP>


Видимо дело в том что это равнозначно:
    struct X
    {
        const char *chars;
        
        constexpr X() : chars("abc") {}
    };
Re[4]: Чудны способности твои, о стандарт !
От: wander  
Дата: 02.10.15 13:10
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>>
EP>>    struct X
EP>>    {
EP>>        const char *chars = "abc";
EP>>    };
EP>>


EP>Видимо дело в том что это равнозначно:

EP>
EP>    struct X
EP>    {
EP>        const char *chars;
        
EP>        constexpr X() : chars("abc") {}
EP>    };
EP>


Да, именно.
Насчет constexpr функции с auto — то это работает только в С++14.
А приемы эти я изобретал еще на C++0x.
Можно применить вот такой вариант для С++11, если все еще есть сомнения насчет легитимности.
struct X
{
    typedef decltype("abc") type;
    static constexpr type chars() { return "abc"; }
};
Re[3]: Чудны способности твои, о стандарт !
От: wander  
Дата: 02.10.15 13:19
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Я в подобном случае
Автор: Evgeny.Panasyuk
Дата: 12.10.14
использовал constexpr метод:


Спасибо за ссылку. Не видел.
Сразу уже вижу, что ты исследование провел более полное, чем я в свое время
Re[5]: Чудны способности твои, о стандарт !
От: Evgeny.Panasyuk Россия  
Дата: 02.10.15 13:33
Оценка:
Здравствуйте, wander, Вы писали:

W>Насчет constexpr функции с auto — то это работает только в С++14.


Его можно без проблем заменить на const char *, auto там исключительно для удобства.
Там всё равно размер вычисляется через:
constexpr std::size_t c_str_length(const char *x)
{
    return find(x, '\0') - x;
}


W>Можно применить вот такой вариант для С++11, если все еще есть сомнения насчет легитимности.

W>
W>struct X
W>{
W>    typedef decltype("abc") type;
W>    static constexpr type chars() { return "abc"; }
W>};
W>


Или другой C++11 вариант:
struct X
{
    static constexpr auto chars() -> decltype("abc")
    {
        return "abc";
    }
};
Re[6]: Чудны способности твои, о стандарт !
От: wander  
Дата: 02.10.15 13:43
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Его можно без проблем заменить на const char *, auto там исключительно для удобства.


Точно не вспомню пример, но иногда это не работало.
Возможно это были временные баги компиляторов, не помню уже.
Последний раз с этим всерьез возился кажется в 12 году.
Re[6]: Чудны способности твои, о стандарт !
От: wander  
Дата: 02.10.15 13:57
Оценка:
Здравствуйте, Evgeny.Panasyuk:

В общем, я тогда остановился вот на таком варианте, как на наиболее подходящем:
    #define DECL_STR(str) \
        struct { char const * chars = (str); }

    using test = DECL_STR("test");
Re[3]: Чудны способности твои, о стандарт !
От: wander  
Дата: 05.10.15 09:47
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Clang и GCC компилируют.


VS 2015, кстати, тоже.
Вообще весь код без изменений (почти, см. ниже) работает в 2015.
Проверял тут: http://webcompiler.cloudapp.net/

Изменения:
template <typename Str, size_t ...I>
struct string_gen<Str, ::ct::indices<I...>>
    : ::ct::string<Str().chars[I]...> // Str{} -> Str()
{ };
Re[5]: Чудны способности твои, о стандарт !
От: rus blood Россия  
Дата: 05.10.15 11:37
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>В том-то и дело, что тип таких переменных не зависит от их специализации. Просто у этих переменных имена сложные, составные.


А в len<meter> можно явно/неявно присвоить len<centimeter> ?
Имею скафандр — готов путешествовать!
Re[6]: Чудны способности твои, о стандарт !
От: B0FEE664  
Дата: 05.10.15 12:00
Оценка: 4 (1) +1
Здравствуйте, rus blood, Вы писали:

BFE>>В том-то и дело, что тип таких переменных не зависит от их специализации. Просто у этих переменных имена сложные, составные.

RB>А в len<meter> можно явно/неявно присвоить len<centimeter> ?

Да. Можно явно присвоить. len<meter> = len<centimeter>; присвоит значение len<centimeter> в len<meter>. Поэтому я и говорю, что это просто имена переменных.
И каждый день — без права на ошибку...
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.