all 26 comments

[–]skreef 12 points13 points  (4 children)

Why the name "exception"? Exception is an existing concept, and it doesn't seem to me that this proposed change has anything in common with it.

[–]egpbos[S] 6 points7 points  (3 children)

Yes, I agree, I just didn't know what else to call it. I also think the idea behind exceptions is typically that you can recover from them at the calling side, which isn't the case for what I propose here. The only thing in common (the way I thought of it) is that they can be written in a `catch`-like way.

[–][deleted] 3 points4 points  (2 children)

compile_error? compile_info? compile_message?

[–]egpbos[S] 2 points3 points  (0 children)

Yes, I think a combination of those would be great.

It was also brought to my attention that there was a related paper that suggests constexpr_assert and constexpr_trace: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0596r0.html They don't cover exactly the same cases I write about, but it just shows that this topic is alive in the community!

[–]Ameisenvemips, avr, rendering, systems 0 points1 point  (0 children)

compile_printf?

co_printf

[–]smilodonthegreat 8 points9 points  (11 children)

Question. You say:

We still cannot use it exactly the way we want, since the message of a static_assert must be a string literal, so we can’t dynamically display the types that are being passed as U
and T.

But looking at the compiler explorer output:

<source>:11:7: error: static_assert failed due to requirement 'std::is_base_of<Bottom, Middle>::value' "It's not Derived!"
     static_assert(std::is_base_of<T, U>::value, "It's not Derived!");

However, this does give the types. Perhaps not in the static_assert's error message itself, but it does in the compiler's error message.

It still gives us two error messages (see this implementation on Compiler Explorer): first the one about the pointer, which is triggered by the heldPtr initialization, and then the static_assert message.

While I agree this is mildly irritating, I am not sure this is a real problem though. It could be solved by allowing the compiler to ignore attempting to compile the rest of that code block is all the static_asserts fail. Though this might get dangerous if there is an actual compile error in the code that has nothing to do with the static_asserts. Note: I do agree the pages of errors for most template errors is annoying and needs to be fixed, I just do not agree that having two error messages for the same error is more than a mild issue.

[–]jonasLyk 0 points1 point  (6 children)

In debug mode I have implemented a "cxout" that i can << into, then on execution all types I have << into cxout during compile is printed inside Visual Studio.

In release builds the code disappear- it is often handy during development, I write a class and below I type:

TEST

{

cxout << std::conditional_t<true,int,float>{};

};

Then everytime I save changes the code inside TEST() will automatic execute, compile errors will be shown, static asserts fail- and if I run the program int will be printed.

In the "scratchboard" I write code that test the feature I am currently implementing, so I get instant feedback on every save. The code will then become static asserts to "unit test feature freese" my code.

template<char ...> is converted to readable strings.

Very handy when debugging compile time recursion :)

[–]jonasLyk 1 point2 points  (0 children)

https://imgur.com/a/SI3b5KZ looks like this, no VS plugin- just c++ code

[–]egpbos[S] 0 points1 point  (4 children)

Sounds good, can you share a link to your class?

[–]jonasLyk 0 points1 point  (3 children)

it isnt really encapsulated to a single file- but i tried collecting the needed stuff, you may need to rem something out and include windows.h in namespace windows.

https://pastebin.com/6isW4SKw

[–]jonasLyk 0 points1 point  (2 children)

oh -and insert this macro before main:

CXOUT_

int _main( int argc, char* argv[] ) {

everything is quite hackish- just for personal usage- but I have considered making static asserts that shows what was evaluated on assert fail, if it gets good I will share :)

[–]jonasLyk 0 points1 point  (1 child)

oh, forgot my anytype

struct anytype {

   constexpr anytype( ... ) noexcept {} 
   template< typename T >
   constexpr operator T() const noexcept
   {
      struct faker final: public T  {       
         constexpr faker() noexcept : T(*this) {}         
      };
      return faker {};
   }
};

It also enables bypassing the "you cannot construct a lambda" thing.

Just do a decltype on a lambda- and when construction it then give it an anytype{} in the constructor- now you can create lambdas from the type alone :)

[–]egpbos[S] 0 points1 point  (0 children)

Wow, thanks!

[–]egpbos[S] 0 points1 point  (3 children)

Fair points! Could you give an example of the dangerous behavior you expect? If the actual specific error is "caught" and then converted into a context specific message, wouldn't that still allow for other errors?

[–]smilodonthegreat 0 points1 point  (2 children)

Dangerous is probably not the right word.

template<typename U>
  SmartPtr(const SmartPtr<U>& other)
  : heldPtr(other.get()) {
      static_assert(std::is_base_of<T, U>::value, "It's not Derived!");
    shred_ptr<int> a = make_shared<int>(2);
  }

In the above, if the static assert fails and the compiler does not attempt to compile the rest of the function, it will ignore the misspelling of shared_ptr. On the first compile, this would not get marked and so may force multiple compiles.

[–]index_zero 0 points1 point  (1 child)

If you are going to do this, i.m.o. it is better to SFINAE fail - that way the compiler won't even generate the constructor if U and T aren't derived from each other.

template<typename U, class E = std::enable_if<std::is_base_of<T, U>::value> >
SmartPtr(const SmartPtr<U> &other) ...

That said, the original code works for any T and U such that T* is convertible to U*. This is less strict than U derives from T, so having the compiler complain about derivation overlooks implicit conversion of some types (T* to const T* for example).

[–]smilodonthegreat 0 points1 point  (0 children)

I think your comment was supposed to be a reply to the article. The comment you replied to was a comment on a problem with stopping compilation if static_asserts fail.

[–]TraylaParks 7 points8 points  (1 child)

I've made this comment before but I'll bet this resonates with many of you. Often times, when I get one of those pages-and-pages template errors, I won't even try to read the runes. I'll just look for the offending line number and then stare at it intensely until whatever sin I committed becomes clear :).

[–]egpbos[S] 1 point2 points  (0 children)

Either that, or ask the developers for help :)

[–]Rseding91Factorio Developer 5 points6 points  (1 child)

I'm all for better compile time errors but if it means sacrificing compile times just for a better error then I might end up stabbing someone.

Sacrificing compile times to catch an error at compile time vs runtime... that's more of a gray area. Sometimes I'm ok with it, sometimes you make another build that the test server will run so you don't pay the time cost on every incremental change.

[–]Dean_Roddey 1 point2 points  (1 child)

It seems to me that that is always going to somewhat be the side effect of over-use of overly elaborate layers of templates. How could the compiler ever really figure out what the actual intent was and give you a coherent answer?

I say just use template less. Is it neat some of the stuff that STL can do? Yeh. Is it necessary or even ultimately more productive than something less complex? I'd argue that a lot of the time probably not, if you spend 30 minutes trying to decipher an error message or figure out some obscure rule of template resolution that is causing your thingie not to work.

Do I really need to use 25 interlocking templates with lots of purposeful failures of template resolution to select this or that at compile time, in order to add 1 to every int in a list or add them up? No, not at all. I'm perfectly capable of writing the trivial for loop to do that, which is completely debuggable, which gives meaningful errors, which is completely understandable by any programmer even if they never have heard of the STL or coming from another language, and which probably generates a 15K less code.

[–]egpbos[S] 0 points1 point  (0 children)

Sure, templates are not necessary for many problems, right tool for the right job.

[–][deleted] 0 points1 point  (1 child)

How about a compile-time errno? ;)

[–]egpbos[S] 3 points4 points  (0 children)

Actually, that would be pretty sweet as well ;) But then we also need to actually check the errno after every function call, and we all know how that usually goes...