Compile Time String in C++14

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
15 messages Options
Reply | Threaded
Open this post in threaded view
|

Compile Time String in C++14

TONGARI J
Hi all,

I found a way to make CT strings in C++14, with the help of generic lambda.

FYI, Ábel Sinkovics has presented a c++11 CT string in his
mpllibs::metaparse[1] lib before, but it requires some PP trick which has a
predefined limit MPLLIBS_LIMIT_STRING_SIZE.

The new trick doesn't suffer from the limitation, but it assumes a
null-terminated string.
It's fairly simple, see below:

```c++
    template<char...>
    struct str {};

    template<char c>
    struct ch {};

    template<int n>
    using idx = std::integral_constant<int, n>;

    template<class S, char c, char... cs>
    auto accum(S s, ch<c>, str<cs...>)
    {
        return accum(s, s(idx<sizeof...(cs) + 1>()), str<cs..., c>());
    }

    template<class S, char... cs>
    auto accum(S, ch<0>, str<cs...> ret)
    {
        return ret;
    }

    template<class S>
    auto make_str(S s)
    {
        return accum(s, s(idx<0>()), str<>());
    }

    #define LIT(s) make_str([](auto idx) { return ch<s[idx.value]>(); })
```

Then you can use `doSomething(LIT("abc"))` in your code.
What do you think?

[1] http://abel.web.elte.hu/mpllibs/metaparse/

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Compile Time String in C++14

Louis Dionne
TONGARI J <tongari95 <at> gmail.com> writes:

>
> Hi all,
>
> I found a way to make CT strings in C++14, with the help of generic lambda.
>
> FYI, Ábel Sinkovics has presented a c++11 CT string in his
> mpllibs::metaparse[1] lib before, but it requires some PP trick which has a
> predefined limit MPLLIBS_LIMIT_STRING_SIZE.
>
> The new trick doesn't suffer from the limitation, but it assumes a
> null-terminated string.
> It's fairly simple, see below:
>
> ```c++
>     template<char...>
>     struct str {};
>
>     template<char c>
>     struct ch {};
>
>     template<int n>
>     using idx = std::integral_constant<int, n>;
>
>     template<class S, char c, char... cs>
>     auto accum(S s, ch<c>, str<cs...>)
>     {
>         return accum(s, s(idx<sizeof...(cs) + 1>()), str<cs..., c>());
>     }
>
>     template<class S, char... cs>
>     auto accum(S, ch<0>, str<cs...> ret)
>     {
>         return ret;
>     }
>
>     template<class S>
>     auto make_str(S s)
>     {
>         return accum(s, s(idx<0>()), str<>());
>     }
>
>     #define LIT(s) make_str([](auto idx) { return ch<s[idx.value]>(); })
> ```
>
> Then you can use `doSomething(LIT("abc"))` in your code.
> What do you think?

This is genius, thanks a lot for posting. I've been searching for a
way to do this to implement compile-time strings in Boost.Hana[1].

Louis

[1]: https://github.com/ldionne/hana
Reply | Threaded
Open this post in threaded view
|

Re: Compile Time String in C++14

TONGARI J
Hi Louis,

2014-08-26 0:01 GMT+08:00 Louis Dionne <[hidden email]>:
>
> This is genius, thanks a lot for posting. I've been searching for a
> way to do this to implement compile-time strings in Boost.Hana[1].
>

Glad to hear :)

Actually I've found an even simpler way:

```
template<char...>
struct str {};

template<std::size_t... Ns, class S>
auto make_str_impl(std::index_sequence<Ns...>, S s)
{
    return str<s.get()[Ns]...>();
}

template<class S>
auto make_str(S s)
{
    return make_str_impl(std::make_index_sequence<sizeof(s.get()) - 1>(),
s);
}

#define LIT(s) make_str([]
     \
{
    \
    struct
     \
    {
    \
        static constexpr decltype(auto) get()
    \
        {
    \
            return s;
    \
        }
    \
    } _;
     \
    return _;
    \
}())
```

Cheers

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Compile Time String in C++11/C++14

Adam Wulkiewicz
Tongari J wrote:
> Hi Louis,
>
> 2014-08-26 0:01 GMT+08:00 Louis Dionne <[hidden email]>:
>> This is genius, thanks a lot for posting. I've been searching for a
>> way to do this to implement compile-time strings in Boost.Hana[1].
>>
> Glad to hear :)
>
> Actually I've found an even simpler way:

Nice! And with some small tweaks it works in C++11, assuming you have
some implementation of index_sequence:

```
     template<char...>

     struct  str  {};

     template<std::size_t...  Ns,  class  S>

     auto  make_str_impl(index_sequence<Ns...>,  S)  ->  decltype(str<S::get()[Ns]...>())

     {

          return  str<S::get()[Ns]...>();

     }

     template<class  S>

     auto  make_str(S)  ->  decltype(make_str_impl(make_index_sequence<sizeof(S::get())  -  1>(),  S()))

     {

          return  make_str_impl(make_index_sequence<sizeof(S::get())  -  1>(),  S());

     }

     #define  LIT(s)  make_str([]()  \

     {  \

          struct  \

          {  \

              static  constexpr  decltype(s)  get()  \

              {  \

                  return  s;  \

              }  \

          }  _;  \

          return  _;  \

     }())

```

Well VS2013 complains about a static data member in locally defined
class but I assume it's a VS bug.

Regards,
Adam

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Compile Time String in C++11/C++14

TONGARI J
2014-08-26 19:43 GMT+08:00 Adam Wulkiewicz <[hidden email]>:

> Tongari J wrote:
>
>> Hi Louis,
>>
>> 2014-08-26 0:01 GMT+08:00 Louis Dionne <[hidden email]>:
>>
>>> This is genius, thanks a lot for posting. I've been searching for a
>>> way to do this to implement compile-time strings in Boost.Hana[1].
>>>
>>>  Glad to hear :)
>>
>> Actually I've found an even simpler way:
>>
>
> Nice! And with some small tweaks it works in C++11, assuming you have some
> implementation of index_sequence:
>

I believe this still requires C++14 for returning the local struct in
lambda, which is not a one-liner.

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Compile Time String in C++11/C++14

Adam Wulkiewicz
Tongari J wrote:

> 2014-08-26 19:43 GMT+08:00 Adam Wulkiewicz <[hidden email]>:
>
>> Tongari J wrote:
>>
>>> Hi Louis,
>>>
>>> 2014-08-26 0:01 GMT+08:00 Louis Dionne <[hidden email]>:
>>>
>>>> This is genius, thanks a lot for posting. I've been searching for a
>>>> way to do this to implement compile-time strings in Boost.Hana[1].
>>>>
>>>>   Glad to hear :)
>>> Actually I've found an even simpler way:
>>>
>> Nice! And with some small tweaks it works in C++11, assuming you have some
>> implementation of index_sequence:
>>
> I believe this still requires C++14 for returning the local struct in
> lambda, which is not a one-liner.

I'm not that well versed in the ways of the standard but I do know that
it compiles in GCC 4.8.1, 4.8.3, 4.9.1 and clang 3.4 with -std=c++11
switch. So either those compilers are buggy or C++14 isn't really required.
As said before MSVC2013 complains about something else.

Regards,
Adam

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Compile Time String in C++11/C++14

Adam Wulkiewicz
Adam Wulkiewicz wrote:

> Tongari J wrote:
>> 2014-08-26 19:43 GMT+08:00 Adam Wulkiewicz <[hidden email]>:
>>
>>> Tongari J wrote:
>>>
>>>> Hi Louis,
>>>>
>>>> 2014-08-26 0:01 GMT+08:00 Louis Dionne <[hidden email]>:
>>>>
>>>>> This is genius, thanks a lot for posting. I've been searching for a
>>>>> way to do this to implement compile-time strings in Boost.Hana[1].
>>>>>
>>>>>   Glad to hear :)
>>>> Actually I've found an even simpler way:
>>>>
>>> Nice! And with some small tweaks it works in C++11, assuming you
>>> have some
>>> implementation of index_sequence:
>>>
>> I believe this still requires C++14 for returning the local struct in
>> lambda, which is not a one-liner.
>
> I'm not that well versed in the ways of the standard but I do know
> that it compiles in GCC 4.8.1, 4.8.3, 4.9.1 and clang 3.4 with
> -std=c++11 switch. So either those compilers are buggy or C++14 isn't
> really required.
> As said before MSVC2013 complains about something else.

Actually besides the complaining about static data member in local
struct (get() member function) MSVC2013 indeed complains about the
returning of local struct. AFAIU all of the presented implementations
should fail on this compiler.

Regards,
Adam

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Compile Time String in C++11/C++14

Adam Wulkiewicz
Adam Wulkiewicz wrote:

> Adam Wulkiewicz wrote:
>> Tongari J wrote:
>>> 2014-08-26 19:43 GMT+08:00 Adam Wulkiewicz <[hidden email]>:
>>>
>>>> Tongari J wrote:
>>>>
>>>>> Hi Louis,
>>>>>
>>>>> 2014-08-26 0:01 GMT+08:00 Louis Dionne <[hidden email]>:
>>>>>
>>>>>> This is genius, thanks a lot for posting. I've been searching for a
>>>>>> way to do this to implement compile-time strings in Boost.Hana[1].
>>>>>>
>>>>>>   Glad to hear :)
>>>>> Actually I've found an even simpler way:
>>>>>
>>>> Nice! And with some small tweaks it works in C++11, assuming you
>>>> have some
>>>> implementation of index_sequence:
>>>>
>>> I believe this still requires C++14 for returning the local struct in
>>> lambda, which is not a one-liner.
>>
>> I'm not that well versed in the ways of the standard but I do know
>> that it compiles in GCC 4.8.1, 4.8.3, 4.9.1 and clang 3.4 with
>> -std=c++11 switch. So either those compilers are buggy or C++14 isn't
>> really required.
>> As said before MSVC2013 complains about something else.
>
> Actually besides the complaining about static data member in local
> struct (get() member function) MSVC2013 indeed complains about the
> returning of local struct. AFAIU all of the presented implementations
> should fail on this compiler.

A remark, the first mentioned MSVC error is caused by the lack of the
support for 'constexpr' keyword in this compiler.

>
> Regards,
> Adam



_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Compile Time String in C++14

Paul Fultz II
In reply to this post by TONGARI J
> Then you can use `doSomething(LIT("abc"))` in your code.
> What do you think?

I think I'm missing something. Isn't the point of compile-time strings to
parse them or process them at compile-time? The `make_str` is not `constexpr`
and neither is the lambda. So it can't be passed to a `constexpr` function nor
can it be passed to template parameter because of the lambda. Unless there is
something going on that I'm unaware of.

Paul Fultz II
Reply | Threaded
Open this post in threaded view
|

Re: Compile Time String in C++14

Abel Sinkovics
In reply to this post by TONGARI J
Hi,

On 2014-08-26 04:22, TONGARI J wrote:

> Actually I've found an even simpler way:
>
> ```
> template<char...>
> struct str {};
>
> template<std::size_t... Ns, class S>
> auto make_str_impl(std::index_sequence<Ns...>, S s)
> {
>      return str<s.get()[Ns]...>();
> }
>
> template<class S>
> auto make_str(S s)
> {
>      return make_str_impl(std::make_index_sequence<sizeof(s.get()) - 1>(),
> s);
> }
>
> #define LIT(s) make_str([]
>       \
> {
>      \
>      struct
>       \
>      {
>      \
>          static constexpr decltype(auto) get()
>      \
>          {
>      \
>              return s;
>      \
>          }
>      \
>      } _;
>       \
>      return _;
>      \
> }())
> ```

Compile time strings without the length limit would be great. The way
I'm using them is instantiating template classes with the string as a
template argument. So the way I'd use it would be something like:

----
template <class String>
struct metafunction_expecting_a_string;

metafunction_expecting_a_string<decltype(LIT("Hello"))>
----

I've tried your solutions with the latest Clang in svn, but the compiler
complained about having a lambda expression in an unevaluated operand.

Did you manage to use these strings as template arguments somehow?

Regards,
   Ábel


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Compile Time String in C++14

Mikael Persson
In reply to this post by Paul Fultz II
I also fail to see the advantage of this implementation. I mean, it still
uses index_sequence (or, in the first version, a variadic template
parameter pack of chars), which is the main problem with using compile-time
strings; it's the common problem in most real constexpr string
implementations, as far I as know. An implementation of a constexpr string
class that would avoid forming this type of very costly (non-scalable)
template instantiation is starting to become interesting from a usability
point of view, but short of that, it's hard to justify using it for
anything but trivial tasks. That was just a remark.

And as Paul remarks, you have obviously omitted the "constexpr" specifier
in many places.

So, can you explain how your implementation differs or improves upon
existing constexpr string implementations?

Here is a short list of existing compile-time string implementations (in
case some people reading this are not familiar with the options available
to solve that problem):

Scott Schurr's very simple demo of "str_const":
https://github.com/boostcon/cppnow_presentations_2012/blob/master/wed/schurr_cpp11_tools_for_class_authors.pdf?raw=true

Which is very similar (and better explained) to Andrzej Krzemienski's
"StrWrap":
http://akrzemi1.wordpress.com/2011/05/11/parsing-strings-at-compile-time-part-i/

Or Sprout's very comprehensive implementation (basically a compile-time
implementation of std::string):
https://github.com/bolero-MURAKAMI/Sprout/blob/master/sprout/string/string.hpp

Or yet again, my own more light-weight version which supports compile-time
concatenation, hashing and int-to-string functions only:
https://github.com/mikael-s-persson/ReaK/blob/pp_dev/src/ReaK/core/base/cnst_string.hpp

which I explain in more details here (warning: it's a bit more of a
tutorial for beginners, not aimed at experts):
https://www.daniweb.com/software-development/cpp/code/482276/c11-compile-time-string-concatenation-with-constexpr

@ Abel:

> So the way I'd use it would be something like:
> ...
> Did you manage to use these strings as template arguments somehow?

AFAIK, you can use any of the above-listed implementations of constexpr
strings in the way that you showed, but not the one discussed here.

Compile-time strings are not a new thing, they are simply not very
practical because they cause so much compilation overhead (because of
compile-time char-by-char operations, deep compile-time recursions, and
costly template instantiations), IMHO.

Cheers,
Mikael Persson.

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Compile Time String in C++14

Adam Wulkiewicz
Mikael Persson wrote:
> Here is a short list of existing compile-time string implementations (in
> case some people reading this are not familiar with the options available
> to solve that problem):
>
<snip>

Thanks for the links and valuable info!

I already played before with Scott Schurr's implementation but wasn't
able to do with it everything I wanted (see below).

>
> @ Abel:
>
>> So the way I'd use it would be something like:
>> ...
>> Did you manage to use these strings as template arguments somehow?
> AFAIK, you can use any of the above-listed implementations of constexpr
> strings in the way that you showed, but not the one discussed here.

Do you know about some example where this is actually done? I can't see
a straightforward way to pass such constexpr string as a template
parameter and do something reasonable with it internally. In particular
to generate a C++ type from a string literal. I think this is the most
important concept. Correct me if I'm wrong but it seems to me that if
you're dealing with constexpr strings you must handle them in constexpr
functions and AFAIU that's not what Abel wanted. The technique proposed
by TONGARI allows to generate types, at least in some cases, still not
in Abel's case. Or am I missing something?

Regards,
Adam

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Compile Time String in C++14

TONGARI J
In reply to this post by Mikael Persson
2014-08-27 8:24 GMT+08:00 Mikael Persson <[hidden email]>:

> I also fail to see the advantage of this implementation. I mean, it still
> uses index_sequence (or, in the first version, a variadic template
> parameter pack of chars), which is the main problem with using compile-time
> strings; it's the common problem in most real constexpr string
> implementations, as far I as know. An implementation of a constexpr string
> class that would avoid forming this type of very costly (non-scalable)
> template instantiation is starting to become interesting from a usability
> point of view, but short of that, it's hard to justify using it for
> anything but trivial tasks. That was just a remark.
>
> And as Paul remarks, you have obviously omitted the "constexpr" specifier
> in many places.
>
> So, can you explain how your implementation differs or improves upon
> existing constexpr string implementations?
>

My intention was to implement a compile-time Spirit that operates on CT
string and returns different types depending on the input, so my usage is a
bit different.
Unlike the way Abel used in his metaparse, where every thing is a template
parameter, as `metafunction_expecting_a_string<decltype(LIT("Hello"))>`,
I'd like use `string(LIT("Hello"))`, which I think is more like the way
Boost.Hana adopts.

I don't think constexpr is needed in my case, because what I need is the
type (i.e. the constants encoded in the type), not the value.
For example, here's how I implement the string_parser:

```c++
struct string_parser : parser<string_parser>
{
    template<class S>
    struct parse_impl;

    template<char... cs>
    struct parse_impl<str<cs...>>
    {
        template<char... etc>
        static auto apply(str<cs..., etc...>)
        {
            return success(str<etc...>(), str<cs...>());
        }

        template<class S>
        static auto apply(S s)
        {
            return fail(s);
        }
    };

    template<class S, class Str>
    static auto parse(S s, Str)
    {
        return parse_impl<Str>::apply(s);
    }
};

constexpr string_parser string = {};
```

which can then be used:

```
string(LIT("123")).parse(LIT("1234")); // returns (true_, str<'4'>,
str<'1', '2', '3'>), a pack of (result, rest-input, attr)
```


> Here is a short list of existing compile-time string implementations (in
> case some people reading this are not familiar with the options available
> to solve that problem):
>
> Scott Schurr's very simple demo of "str_const":
>
> https://github.com/boostcon/cppnow_presentations_2012/blob/master/wed/schurr_cpp11_tools_for_class_authors.pdf?raw=true
>
> Which is very similar (and better explained) to Andrzej Krzemienski's
> "StrWrap":
>
> http://akrzemi1.wordpress.com/2011/05/11/parsing-strings-at-compile-time-part-i/
>
> Or Sprout's very comprehensive implementation (basically a compile-time
> implementation of std::string):
>
> https://github.com/bolero-MURAKAMI/Sprout/blob/master/sprout/string/string.hpp
>
> Or yet again, my own more light-weight version which supports compile-time
> concatenation, hashing and int-to-string functions only:
>
> https://github.com/mikael-s-persson/ReaK/blob/pp_dev/src/ReaK/core/base/cnst_string.hpp
>
> which I explain in more details here (warning: it's a bit more of a
> tutorial for beginners, not aimed at experts):
>
> https://www.daniweb.com/software-development/cpp/code/482276/c11-compile-time-string-concatenation-with-constexpr


Thanks for the information, I know some of them, will check out later.


> @ Abel:
>
> > So the way I'd use it would be something like:
> > ...
> > Did you manage to use these strings as template arguments somehow?
>
> AFAIK, you can use any of the above-listed implementations of constexpr
> strings in the way that you showed, but not the one discussed here.
>

I think Abel needs the value encoded in the type, so something like
Sprout's string may not help.

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Compile Time String in C++14

Mikael Persson
> I think Abel needs the value encoded in the type

Ok. Now I get it. That was the critical piece of information I did not get
from the previous communications. Thanks for clearing that up.

I think that the term "compile-time string" led to the confusion, because
there is already a pretty good tradition of compile-time string
construction and manipulation. In the sense that it pre-computes or
pre-parses strings during compilation. The idea of computing types out of
compile-time strings is a different problem altogether. And I would not
call this "compile-time strings", I would call them "meta-programming
strings", because that's what they are, strings used to construct types
(i.e., meta-programming). The terms used completely threw me off there.

> I can't see a straightforward way to pass such constexpr string as a
template parameter and do something reasonable with it internally.

Yeah, sorry, my bad. I completely missed that point somehow. I guess the
"metafunction" should have been a hint, but I guess it must be getting
late. You're right, the barrier to cross from constexpr to template
parameters is pretty much impenetrable as far as strings are concerned
(btw, you can easily use a constexpr string to generate a unique hash that
can be used to construct a type that is unique to the original string, but
I guess that's not quite what you are looking for). So yeah, you're right,
you cannot do that with compile-time strings.

> My intention was to implement a compile-time Spirit that operates on CT
string and returns different types depending on the input, so my usage is a
bit different.

Ok, I see, you want to remain purely TMP the whole way, and the end-goal of
your expressions are types, not strings. (BTW, if your goal is compile-time
string parsing, then you ought to be using constexpr strings, not TMP
strings, because as much overhead as there is when using constexpr for this
kind of work, I would imagine the overhead of using TMP for this would be
astronomical, intractable)

In that perspective, I can totally see how your MACRO might make sense, if
it was legal. The trick really is to circumvent the standard rules that
essentially make it impossible to map a string literal into a template
parameter pack of chars. I think that with your use of the lambda
expression, you tried to cheat your way out of those rules, but there is a
specific rule for that loophole, $5.1.2/2:
"A lambda expression shall not appear in an unevaluated operand"
This means that you cannot try to smuggle a string literal out of an
unevaluated lambda in order to get it into a type. And the rationale for
this rule is specifically aimed at avoiding opening the can of worms that
it would be if people were allowed to do meta-programming from lambda's,
because you could hide all sorts of monstrous things in a lambda and then
pull them out into templates.

That pretty much closes off your proposed avenue, doesn't it? If you got
this to work, I would have to imagine it is due to working with a
non-compliant compiler.

I tried your code with both clang 3.5 and GCC 4.8.2, and they both reject
it on the grounds of the rule that I just stated.

You'll have to find another way to do this. It is a tough problem. And when
it comes to working with string literals, I'm afraid the pre-processor is
pretty much all you have, as Abel evidently realized when creating the
MPLLIBS_STRING macro.

I personally find this problem very frustrating because we all know that
all compilers could do a lot more work with strings at compile-time, either
as constexpr or as template parameters. And we wouldn't have to resort to
such ridiculous and inefficient solutions as macro expansions or template
instantiations with packs of chars.

Sorry again for my initial misunderstanding of your aim with the code you
showed.

Cheers,
Mikael Persson

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Compile Time String in C++14

Adam Wulkiewicz
Mikael Persson wrote:
>
> $5.1.2/2:
> "A lambda expression shall not appear in an unevaluated operand"
> ...
> That pretty much closes off your proposed avenue, doesn't it?

Only in the case of metaprograms. The proposed technique still may be
used in a place where a lambda expression could be evaluated, so when
it's passed as a function parameter and the return type is deduced in
compile-time.

Regards,
Adam

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost