Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Just how constexpr is C++20’s std:string? (quuxplusone.github.io)
120 points by jandeboevrie on Sept 8, 2023 | hide | past | favorite | 103 comments


> libstdc++ rejects the following code (Godbolt [1]). (Microsoft accepts, but I think that might be an MSVC bug.)

This is not a bug in MSVC, rather it is due to MSVC implementing std::string SSO differently than gcc or clang. Instead of initializing `data` as pointing to the internal buffer for small strings, MSVC uses the string's capacity to determine whether to access `data` or the internal storage [2].

Hence this code compiles, as it's not initializing the string using a stack address. [3]

[1]: https://godbolt.org/z/1ErrKjdbq

[2]: https://devblogs.microsoft.com/oldnewthing/20230803-00/?p=10...

[3]: https://godbolt.org/z/1MaxdGfvj


That's fine that it is not a bug, but it seems like a compatibility nightmare.


It can lead to "fun" optimizations.

I spent a while porting a codebase that was written by someone far more clever then smart. They had discovered that you could clear a string under MSVC by `memset()`ing over it.

Obviously, this exploded in linux. That was a fun bug to track down.


> They had discovered that you could clear a string under MSVC by `memset()`ing over it.

Why was that? Was it too problematic to just call std::string::clear ?


I'm guessing the string was part of a larger struct, and they wanted to clear the entire struct with a single memset.

It's a somewhat common pattern in C, but requires care.


> It's a somewhat common pattern in C (...)

C++ isn't C, though.


How does that even work on MSVC? Wouldn't it leak memory?


> How does that even work on MSVC?

It sets the capacity to zero. MSVC then interprets it as a short string with length 0, which doesn't have an external allocation.

> Wouldn't it leak memory?

Yes, but I assume the "far more clever then smart" programmer didn't notice.


Yeah I wouldn't call that working on MSVC either haha.


Definitely not "working", yeah, but

On MSVC, an all-zero string object is the correct representation for a std::string containing "" (and the memory leak is easy to miss if you don't do it too much).

On both Clang and gcc, an all-zero string object is not using the short-string optimization -- it is a string with size zero, capacity zero, and an external buffer of nullptr. (On clang this happens because capacity is even, and on gcc because &str->buf != &str). Any code that attempts to access its null terminator will dereference the null pointer and crash immediately.


Yeah I get that.


If your string hadn't allocated yet then nope, probably not.


SSO is "small string optimization", in case anyone was wondering.


I find that there's no use case for a constexpr std::string. Maybe I'm just not imaginative enough. If I want a string literal constant, I use string_view which can be constexpr. If somehow I need an actual std::string object, I use a regular const not constexpr. What would be a use case that prompts the author to explore using constexpr with std::string?


Generate a large number of strings and maps programmatically in compile time.

You are pretty much limited to macros and macro-like languages (llvm-tablegen) if you want to do this today.


You can also do the same with templates quite easily, but the resulting compile times are going to be absolutely atrocious.


A bunch of constexpr stuff since C++14 onwards have been added just to make meta-programming more usable.

Users, library writers, and compiler writers all benefit from this in some way.


Maybe I'm just not imaginative enough but what sort of use-case is this sort of thing for?


Parsers, and any text heavy application really.


It enables users to process string at compile time. You can implement a constexpr getRFC3339DateString(int year, int month, int day) -> string and then construct a constexpr string list.


But what can you do with that string list afterwards? You can't store it anywhere to be accessed at run time, can you?


The important part is that all the intermediate strings used during the computation are constexpr, to guarantee that the work happens during compilation.

Also, constexpr symbols can be demoted to regular const as needed by the compiler if necessary, such as when getting a pointer to one. constexpr doesn't mean "compile-time only", it means "compile-time compatible"


Right - you can use the std::string at compile time but since it allocates dynamically you need to copy to a fixed size char[]/std::array to use at runtime.


The constructor of std::string is constexpr in C++20. A runtime call to 'strlen' (or equivalent) can be optimized out by computing the string's length at compile time and initializing the object accordingly.


> The constructor of std::string is constexpr in C++20.

If I recall correctly, from C++20 onward all member functions of std::string are constexpr.


> If I want a string literal constant, I use string_view which can be constexp

That assumes your API accepts string views. How do you pass a string_view (safely) to fopen, or CreateFile (on windows)?


Old Windows APIs are a bit of a mess when it comes to const. So many that use LPWSTR strings rather than an LPCWSTR.


While (I think) I understand your broader point, when would you pass a string created by a constexp to fopen() or CreateFile?


They were examples, they're widely used C apis. Anything that passes through to glibc, windows or posix will have this problem.


I lack the intuition to understand why is it useful to constexpr path names. Could you elaborate?


It's not about path names. It's about passing constexpr strings to c functions that expect a null terminated string. You're totally missing the point.

That said, here's an example:

    // imagine this with string_view
    FILE* openFileInPath(const std::string& path) {
        
        if (path.starts_with("some_dir"))
        {
            return fopen(path.c_str());
        }
    return nullptr;
    }
That function works just fine to only open a path that has a prefix (logic bug on relative paths aside for brevity while posting)

I should be able to pass a constexpr string to that function the same way I can pass a runtime created string.


Oh, I don't doubt I'm missing the point. That's why I'm asking. My C++ experience is from the 1990s. Otherwise I do Python and C.

I think of constexpr functions as something which occur at compile time, and I thought this linked-to piece was about what can take place inside those functions.

As I understand it, you want to create a constexpr string so it can be passed to a char*. The issue is this requires heap allocation (for large enough strings, based on the compiler implementation).

The C programmer in me wonders why you don't "char[]" allocate the static memory buffer, then pass around a string view, so the constexpr doesn't need heap at all.


> As I understand it, you want to create a constexpr string so it can be passed to a char*. The issue is this requires heap allocation (for large enough strings, based on the compiler implementation).

There's a few reasons. Avoiding the heap allocation is one, yep. There just shouldn't be a difference between a compile time string and a runtime string. As a programmer I don't want to have to deal with "which flavor of string am I dealing with".

The issue with passing to a c API (I used fopen, but think of any c library - zlib/gzip, openssl, sqlite) is that the string needs to be null terminated, and string view doesn't guarantee null termination.

    int foo(std::string_view view) {
        return strlen(view.data());
    }

    std::string str("hello world");
    foo(str):
    std::string_view view = str;
    foo(view);
    view.remove_suffix(6); // now view is just "hello" with no null termination
    foo(view); // this is wrong.
The problem isn't _specifically_ constexpr std::string in this case, it's that the tools that you use to generalise working with runtime strings, char buffers, and views aren't fit for purpose.


Thank you for your detailed answer.

In the 1990s there was only one string type - NUL-terminated characters. ;)

I keep reading about all the neat algorithms (and multi-threading) support in modern C++, but the complexity of the decades of history and the difference in mindset between {C, Python} and C++ have made me shy away.

I think I'll keep mum from a distance in future discussions until I decided to (re) take up the language seriously.


> In the 1990s there was only one string type - NUL-terminated characters.

Indeed. If I could wave a magic wand, I would add a fat pointer to the language that we use to represent strings and arrays, and that we can automatically convert to/from const char* at compile time. This would replace string_view and span. All of the functionality of std::string would be available to it, and a call to strlen, or strcpy on it would work via the length, rather than the null terminator.

But alas, no magic wand.


Hard-coding a relative file path seems like an obvious example. For example a config file with settings. The file will always have the same name so heap allocations and mutability are not needed.


My config file paths have either been constant (".dotfile") or required run-time information that could not be constexpr'ed. I don't see how constexpr would be useful.

Then again, I am primarily a Python and C programmer, so there is likely some underlying reasoning I'm missing.


const std::string foo = “.dot file”;

in C++ cannot change after construction, but is not compile time constant. This means that it has to run some code and allocate some memory at runtime.

In contrast constexpr is fully compile time and ends up in the read only portion of the binary. No code code execution or allocations necessary.


I use constexpr char[] for string literal constants just because it's a little more compatible. Some things can't take string_view.


Why not use a std::array? Otherwise won't you a second variable for the string length?


`constexpr char kFoo[] = "bar"`, then kFoo can be used like an std::string and passed into things that take const std::string& or string_view. The length is part of that. Or is that actually doing a copy?


For both const std::string& and std::string_view, you're (potentially) incurring a runtime invocation of strlen.

For const std::string&, you're further incurring a copy in order to create a temporary std::string.


Whoops. As you can maybe tell, the stuff I use C++ for isn't so much about performance.


I think you still need the length though unless you can guarantee the string will never have any nulls in it. In Windows, there are API calls that take strings with embedded nulls (and that are typically terminated with a two nulls in a row).

So you might need to deal with a string that looks like "foo\0bar\0\0".


If it was real constexpr it would be useful, e.g. concatenation is commonly desired and requires awkward workarounds currently.


> I find that there's no use case for a constexpr std::string. Maybe I'm just not imaginative enough.

Putting together strings through string interpolation involving somewhat expensive operations is a common usecase. Given the choice, it's preferable to not have to compute them each and every single time a function is invoked.


Would definitely be more useful if you could actually materialize the eventual result into something that's visible at runtime.

If there was reflection, it would be useful to talk about class names, function names, etc, at compile time.


You can use `std::array<char>` (which, as it happens, is a literal type) as an intermediary when performing materialization. A nice toy example is to build a comma-separated string listing enumerators:

    template<class E> requires std::is_enum_v<E> constexpr std::string_view joinedEnum() {
        constexpr auto generate = [] -> std::string {
            return []<template<class...> class L, class... D>(L<D...>) {
                std::string str;
                (((str += (str.empty() ? "" : ", ")) += D::name), ...);
                return str;
            }(boost::describe::describe_enumerators<E>());
        };
        static constexpr auto arr = [&] {
            constexpr std::size_t N = generate().size();
            std::array<char, N> arr;
            std::char_traits<char>::copy(arr.data(), generate().data(), N);
            return arr;
        }();
        return std::string_view(arr);
    }
Note that you have to generate the constexpr std::string twice; once for its size, once for its contents. But you assume that the compiler can memoize the result.


It's really unfortunate the heap allocation can't be in .ro.data for constexpr std::string. It might be an invasive change, but it would beat the hell out of const char* there for the reason of avoiding a copy onto the heap.


I hate const char*. This notation sucks. Is it

    (const char)*
or

    const (char*)?
i.e. is it a pointer to a char but the memory address it points to is const, or is it a variable pointer that points to a const char.


This explanation from isocpp is the one that has always guided me:

   Read the pointer declarations right-to-left.

       * const X* p means “p points to an X that is const”: the X object can’t be changed via p.

       * X* const p means “p is a const pointer to an X that is non-const”: you can’t change the pointer p itself, but you can change the X object via p.

       * const X* const p means “p is a const pointer to an X that is const”: you can’t change the pointer p itself, nor can you change the X object via p.

    And, oh yea, did I mention to read your pointer declarations right-to-left?
https://isocpp.org/wiki/faq/const-correctness#const-ptr-vs-p...


It is the former, a pointer to a const char. A const pointer to char would be qualified as:

char* const


  const char* const f() const
is the best!


I think

    const char *
and

    char * const
are obvious (whichever the const keyword is closest to).

That rule of thumb doesn't work though for

    char const *
or

    char * const *
though.


A lot of folks write char const * (and T const& in C++), and the "rule of thumb" is to read the declaration right to left. In this case, pointer (to) const char. Works also with multiple consts or levels of indirection.


> char * const

Oof no I HATE this.

I also hate it when people write

    char *a;
because the type is char* (i.e. pointer to a char) and the name of the variable is a.


`char * const a` is a different type from `const char *a`.

In the first, the variable `a` is const, but `*a` is not const. That is, you cannot change the value of `a` but you can change what it points to.

In the second `a` is not const, but `*a` is. That is, you can change `a` but you cannot change the value it points to.

I feel that your confusion is tied up in not understanding how C and C++ type declarations work, as is your insistence on `char* a`.

Dennis Ritchie (the C language designer) intended it to be written as `char *a` and talked about this at length over the years as an important design decision: variable declaration follows _usage_, so it’s `char *a` because the type of `*a` is `char`.

For better or worse, Stroustrup took this design decision into C++.

Sure both will parse in this simple case, but keeping this in mind is also the way that more complex declarations make sense.


> because the type is char* (i.e. pointer to a char) and the name of the variable is a.

But it's not.

  char *a, b;
The type you are assigning is just char. The * only affects a.


constexpr is often difficult to get right.

For example, we use a wide_integer type for 128 and 256-bit integers: https://github.com/ClickHouse/ClickHouse/blob/master/base/ba...

It was developed by a C++ expert to fit into the C++ standard, so it has constexpr everywhere. But for this reason, we cannot use memcpy inside its methods and have to wait for a new standard with constexpr memcpy. Note: memcpy is to satisfy strict aliasing - we can use std::bit_cast instead, but there was some trouble.


I'm still on C++20, so I haven't had a chance to try it yet, bit 'if consteval' should allow fixing this, right?


Thank you! Should work.

PS. When reading about `consteval`, `constinit` you might think, "they (C++ committee) are totally crazy, how they think we can follow"


For many years the best language(s) experts have been trying to have compile time and run time strings live together happily ever. They come tantalizingly close but don't quite succeeded. Maybe it is time to entertain the idea that even if compile and run time strings look so similar as to be almost the same - maybe they are not? Maybe they are actually more dissimilar than they appear? Compile time strings to me look more like symbols table: strings unique and ideally sorted. The offset can be a handle - then uniqueness means we can == or != compare handles rather than strings. If the table is sorted, then < > <= >= compares of symbols via their offsets just works too.


> In real life, there’s basically no reason ever to use constexpr on a stack variable

Wait what? This is what i was doing right now. It defines a local compile time constant and hence is an important mean to express an abstract concept. You could use a macro, but we are told to get rid of them. And a local const variable is an entirely different thing.


I think the idea is that things like that should be static constexpr. They have the same lexical scope but don't take up space on the stack.

Surely any competent optimiser will make them the same though.


> In real life, there’s basically no reason ever to use constexpr on a stack variable; you’ll use it only on globals or as part of the set phrase static constexpr.

Hmm… this is just unnecessarily dogmatic. I use constexpr all the time, for example, for local numerical constants that don’t need to be a global.


Why not use a static local constexpr? That seems like it would be faster to me due to not needing to allocate stack space and copy the value to the stack. You still get the benefit of not needing a global.


If the variable is numeric, and const or constexpr, and you have -O1 or higher enabled, then the compiler will optimize it away.

I don’t think static would add anything under these conditions.


Won't the compiler optimize it away even without constexpr or const? The compiler can see it doesn't get changed.


It should. But const or constexpr also work as documentation.


Is accepting 11-char strings but not accepting 19-char strings for constexpr, specified in the C++ specification, or is this just behavior that different compiler vendors are implementing differently?

If the latter, then it is pretty annoying that non-standard behavior is happening now related to standard libraries implementations and constexpr.


My C++ knowledge is from the old days. What book would one recommend to learn _and understand_ these new fangled concepts like "constexpr", "constinit", std::move, std::unique_ptr, closures, etc.


Tour of C++, written by Bjarne Stroustrouop, exactly for those with prior knowledge .


Much more than rust, that’s for sure, where you are forced to stick everything in a macro or pray the compiler is smart for compile time programming.


Not to derail, because frankly I think it's very weird to even bring Rust up here, but `const fn` is a thing in Rust and is guaranteed to execute at compile time.

It is more limited, no question. But you don't have to hope that the compiler will do things, it is guaranteed to do them.


It’s not any weirder than promoting rust under discussions about c++, unless you think any mention of rust must be positive.

There is pretty much nothing useful, this post included, that can be done with const fn in rust.


> It’s not any weirder than promoting rust under discussions about c++

Feels like a straw man? I never promoted Rust, certainly I didn't bring it up unprompted. I corrected someone who brought it up.

> unless you think any mention of rust must be positive.

Again, feels like a straw man. I said nothing of the sort.

> There is pretty much nothing useful, this post included, that can be done with const fn in rust.

I feel like you're simultaneously upset that I corrected your post... but also you're challenging my point, in an attempt to pursue discussion? It's confusing because you're wrong but there's also an interesting discussion to be had - although I think this post covers the general issue of "what is a pointer at compile time" quite well.


I didn’t accuse you of doing it, I said you only find other people bringing up rust weird if it’s not positive. Unless you want to agree that it’s always weird to bring up rust in a discussion about c++?

I’m not upset about anything. You said I’m wrong, so show me how you use const fn to make a heap allocated string at compile time as done in this post.


I can't understand your first point about bringing Rust up, I'm going to go out on a limb and assume that English may not be your first language.

> You said I’m wrong, so show me how you use const fn to make a heap allocated string at compile time as done in this post.

You:

> where you are forced to stick everything in a macro or pray the compiler is smart for compile time programming.

`const fn` exists. You do not need to "pray the compiler is smart" - you can run code at compile time. No need for macros either.

As I also said, it is not as powerful as C++. You can not do heap allocations with const fn.


> I can't understand your first point about bringing Rust up, I'm going to go out on a limb and assume that English may not be your first language.

The sentiment you're looking for is "talking past each other"

(i'm assuming mods can detach my comment from the thread?)


Rereading that's perhaps the issue, the first time I read it I literally could not comprehend what they were saying, I thought it was an ill formed sentence. I see now that it's just worded poorly and also a very odd thing to bring up.


You thought I accused you of promoting rust. I didn’t. I said that someone who complains when rust is criticized in a conversation about c++ should complain equally when rust is promoted, which should not be a controversial statement if you have any consistency.

We are discussing a compile time string. So how can you use const fn to make a compile time string in rust with no macros?


> I said that someone who complains when rust is criticized in a conversation about c++ should complain equally when rust is promoted

Right, this is a straw man. I've never said anything at all about people criticizing C++ and promoting Rust. You're creating an argument out of thin air and attacking it.

> We are discussing a compile time string.

Incorrect. Here is what you said:

> Much more than rust, that’s for sure, where you are forced to stick everything in a macro or pray the compiler is smart for compile time programming.

In no way did you scope it to creating heap allocating strings. Even the post, which ultimately discusses that, discusses other things.

For example, you can write a substr function (firstName) using a const fn in Rust. No macros, no "praying" that the compiler will optimize things.

It would be very odd to limit the discussion to a `string` since this post focuses on how difficult and complex that is in C++, so using it as a "Rust can't even do this" would be sort of ironic.

Anyways, I've engaged in good faith in this discussion for far too long. Your posts are borderline incoherent, frankly, and I think any reader of this thread will have long gotten the message that your initial post was both irrelevant and incorrect.


I said “it’s not any weirder than promoting rust under discussions about c++”. That’s not claiming you said something about promoting rust, that’s asserting that if you have any consistency then you should find that weird as well.

The title of the post asks how constexpr c++ strings are. I answered with “more than rust’s”. The existence of const fn has yet to prove me incorrect.


easy fix: just make a std::constexpr_string


it has to be something ridiculous like std::basic_string<char, ..., std::argh::boink::constexpr_trait{}()<!?>>


This is just genuienly awful for the user. Put yourself back to the times when you first started to learn C++, maybe programming in general. And now you have to wonder why one of your string constants is just fine and the other one gives you some weird error in allocator.h or something...


Reading this is really making me sigh. What is C++? A language used to make applications or a language for the sake of the language?

It often feels like math professors discussing math for the fun of it, not for solving practical problems.


C++ is a language that was expressive enough to have inside the mechanic that is useful for _implementing_ another language for template metaprogramming, but that another language is built from weird building blocks and they do not look natural at all.

Old presentation https://youtu.be/a0FliKwcwXE?t=31

that when I did watch for the first time did remind me A LOT the memes about different levels of haskell engineers writing factorial function, with people somewhere in the middle implementing their own numerics based on basic number theory things. This presentation is literally exact scenario - you take one thing and start building poor man's programming language using it. And it's horrific.

Problem with C++, that it is extremely slowly addressing is that it doesn't have many facilities to improve that nested informal templated language built on top of templating constructs. C++ is getting them, but it at glacier pace. And when I see that presentation after having exposure to other languages, I just feel that this is so much waste here, it could've been much better if language had better thought put into metaprogramming part, instead of letting random crazy geniuses to build hacks on top of hacks to get something useful.

Unfortunately for C++, it gives significant problem for starting. Because to learn something, it is very useful to look into how standard things, like stdlib is implemented. But what you see, often, is not C++, it is that another _thing_ that is using c++ constructs to have its own thing. That has its own patterns and quirks (and there are many of them, so many).

C++ went into local maxima by letting to do many things by various template tricks, but in the end I think that this is dead end. C++ need real metaprogramming, type level programming, you name it - that can be done in (constrained) C++, not in some fungus that has grown on top of template language.


This seems kinda backwards? C++ has gotten as complex as it has mostly because it’s been the pragmatic default for big, performance sensitive applications consistently for like 30 years and the clear up-and-comer for ~10 more.

Java can be really fast and handle big codebases too, and it’s also been huge for long enough it can buy a beer, and it’s also getting a little hairy.

Uh, other languages in the niche seem to be starting their runs at comparable levels of complexity, if they’re as successful as it looks like, writing them will be quantum chromodynamics in another 30.


> Java can be really fast and handle big codebases too, and it’s also been huge for long enough it can buy a beer, and it’s also getting a little hairy.

IMO Java is so simple you can teach it to any programmer in a day or two. I'm not sure which part are "hairy", maybe frameworks?

Memory order is hard but that's only very rarely used in idiomatic Java, and mostly through the atomic wrappers. Synchronized is simpler than equivalent barriers in almost all other languages.

There aren't that many keywords, no operator overloading, barely any metaprogramming (just annotations), almost no compile flags, etc.

I guess tuning the runtime can be hairy, and some of the standard lib stuff is weirdly over complicated (nio vs io), but as far as languages go, Java has been incredibly conservative. In fact, a lot of Java app dev would probably be easier in practice if it had more features.


I think i understand what you say, but i don't think this is in contrary to my statement of C++ having become a language of which features are often reasoned about for the sake of the language itself in stead of for the sake of the goal of the language, namely writing and maintaining computer programs.


Luckily, one can just ignore all the modern C++ shenanigans and use it as not much more than C with classes and some basic templates for generic containers. Those are too useful to give up.


You can, but the modern stuff (in the last decade or so) have made it so much nicer and more fun to work with.


Is everyone on a team of one in C++ land? I kind of want to learn a bit of C++ but to be honest, I’m a bit scared off by the fact that it’s only workable if you stick to a specific style. If I learn C++, will I be completely lost the moment I change teams? Presumably they’ll have their own set of practices they like to follow that I’ll be expected to be productive with.


Most large teams totally ignore whatever the standard library is doing, because it's largely just bad, over engineered and slow, both to compile and at runtime. They then have their own developed from scratch. It would be funny if it wasn't so sad.

For example, even though both Chromium and Unreal Engine are using C++, good luck finding basically anything high level in common. All of the naming schemes are different, the formatting is different, the high level libraries are unrecognizable. It almost feels like these projects are written in different languages.

Beneath all of that, they do make use of the same low level mechanics. A pointer is still a pointer, no matter what libraries you build on it. A stack allocation is still a stack allocation, passing something by a const reference is still the same, passing universal references to container templates is still the same. Includes work the same everywhere. And so on.

Basically, unlike most other languages, you want to have a solid understanding of these underlying mechanics so you can then quickly understand whatever high level libraries the project decided to build on top and hit the ground running using those. There's gonna be some kind of resizable array thing in every project.

But yes, you're basically starting over re-familiarizing yourself with the language on every large project or framework you explore. I've been using C++ for many years and never once used a std::string, since each framework has its own clearly superior string...


You want to be on a team that contains a C++ expert or two, and they should conscientiously and continuously evolve your code base to incorporate more static time correctness checks and to be more in line with where the standard library is heading.


C++ was accidentally highly expressive. Since it was accidental, much of this expressiveness was only available via convoluted and inscrutable metaprogramming. Nonetheless, this expressiveness turned out to be highly useful in practical scenarios, which established the value of that expressiveness.

If you fast-forward to C++20, metaprogramming is mostly, though not always, concise and easy to follow. The language was redesigned to make template hacks that people found highly useful into first-class features of the language. For reasons of backward compatibility, you can still express these things the old ways. But it isn’t required anymore, there are concise and direct ways of expressing most metaprogramming things you might want to do.

The extreme expressiveness of modern C++ in application domains with unusual requirements remains its greatest strength. For pure systems languages, nothing else can express as much in a type-safe way in so few lines of code, almost entirely due to its metaprogramming facilities.


Don't like it, don't use it. For a lot of us writing stuff in c++, the enhancements are always quite useful.


[deleted]


This is extremely off topic — what does any of this have to do with constexpr strings?


Er, I'm way out of touch with modern c++, what implementation do you buy? Borland used to have tons of little helpers and utils to smooth over some rough edges.

I'd imagine ms vc++ has tons of stuff to support this?

Again, I haven't used a commercial compiler in a while. But that seems like really standard stuff you get with them, yeah?


3rd party libraries provide that functionality, and do it with higher performance than in any other another language


> :

Meant to be ‘::’, of course. (This abomination can only compete with the ML’s double semicolon.)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: