C++ testing for compile errors
Asked Answered
B

5

6

I'm a student, and I'm trying to write and run some test code for an assignment to check it before I turn it in. What I'm trying to do now is test that my code prevents value semantics properly. In my assignment, I have declared for each of my classes its own private copy constructor and assignment operator that have no definition, and so do nothing. When they are called in my test program, I am getting compile errors like I expected. Something like this:

error: 'myClass::myClass(const &myClass)' is private'

error: 'myClass& myClass::operator=(const myClass&)' is private

Is there a way to use try/catch so that my test code will compile and run, but show me that these errors did occur? I've tried:

myClass obj1(...);
myClass obj2(...);
try{
  obj1 = obj2;
  throw 1;
}
catch(int e){
  assert(e==1);
}

but the compiler is still giving me the above errors. Are these not 'exceptions'? Will they not trigger a throw?

If I'm understanding try/catch correctly, it handles runtime errors, not the kind errors I was getting above, correct?

After doing some more research, it seems that there is no (easy) way of testing for certain compile errors natively within C++ (this maybe true for most languages, now that I think about it). I read a post that suggests writing some test code in a scripting language that attempts to compile snippets of C++ code and checks for any errors, and another post that recommends using Boost.Build.

What is the easiest/best way of doing what I'm trying to do?

I looked at the documentation for Boost.Build and it's a bit over my head. If I used it, how would I test that a file, say 'test.cpp' compiles, and maybe handle specific compile errors that occur with 'test.cpp'?

Thanks for your help!

P.S. This is one of my first posts, hopefully I've done "enough" research, and done everything else properly. Sorry if I didn't.

Burro answered 29/3, 2012 at 0:47 Comment(5)
As soon as you compile your program, you get a list of errors. Was that not enough?! If you add a test class, that can't extract more "compile errors" from your code. Test classes are written to (after compilation) run your code and extract semantic errors.Canst
In this case, where I only had a handful of operations that I wanted to test, reading through the compile errors wasn't really a big deal. But what happens when you're working on a big project and there are many operations/behaviors that you want to prevent, operations/behaviors that you want to produce compile errors? Manually reading through and checking for each one seems more than a bit tedious.Burro
Wait, you can't "produce a compile error" for a runtime operation/behavior. The compiler reads your code, makes sure it conforms to the C++ language, lexically, grammatically and semantically, and generates code for it. It only checks for whatever is specified by the C++ language. If the generated code doesn't work, that has nothing to do with the compiler (so it can't give you compile errors)Canst
Compile errors in big projects are not a big deal either. First of all, you always compile as you develop so every time there are few compile errors. Second, compile errors are something you have no choice but to sit down and resolve one by one (otherwise, you don't get an executable) which is not as hard as it sounds.Canst
@Canst it is not enough.Sosthina
I
5

These are compiler errors, not exceptions. Exceptions are a mechanism for programmers to throw run-time errors and catch/handle them. The compiler fails to even build an executable for you to run because it recognizes that the code is malformed and is invalid C++ code.

If you want to make this a run-time error, make the method public/use friends/whatever you need to do to provide access to something and throw an exception in the method's definition, the catch and handle the exception in the calling code.

I don't see a purpose in doing this however. Always prefer a compile-time error to a run-time error. Always.

The C++ standard defines what is valid or invalid code, with some things left as undefined and other things left up to whoever implements the compiler. Any standard compliant C++ compiler will give an error because something does not meet the standard/definition and is thus invalid. The errors are generally to say that something is ambiguous or straight up nonsensical and you need to revise what you've written.

Run-time errors are either crashes or behavior that is unintended and unwanted from the perspective of the user. Compiler errors are the compiler saying "I don't understand what you're saying. This doesn't make sense.". Compiler warnings are the compiler saying "I'll let you do this, but I probably shouldn't. Are you really sure this is what you meant?".

Ingesta answered 29/3, 2012 at 1:3 Comment(0)
S
5

After doing some more research, it seems that there is no (easy) way of testing for certain compile errors natively within C++

I think this might not be the case anymore if you can use C++2a.

As I am currently writing tests for templated code, I also tried to test for compile time errors.

In particular, I want to test for a negative feature, hence provide a guarantee that certain construct will fail to compile. That is possible using c++20 requires expressions as follows:

Simple example

Below, I check that the nonexistent function invalid_function cannot be called on a Struct of type S:

struct S {}; //< Example struct on which I perform the test
template <typename T> constexpr bool does_invalid_function_compile = requires(T a) {
  a.invalid_function();
};
static_assert(!does_invalid_function_compile<S>, "Error, invalid function does compile.");

Note that you could replace the static_assert by the apporpriate function of your testing framework, which records a test error at runtime and hence avoids this compile test to stop other tests from executing.

Example form question

This example can of course be adapted to work with the scenario depicted in the question, which might look approximately like this:

/// Test struct with deleted assignment operator
struct myClass {
  auto operator=(myClass const &) = delete;
};
/// Requires expression which is used in order to check if assigment is possible
template <myClass cl1, myClass cl2> constexpr bool does_assignment_compile = requires() {
  cl1 = cl2;
};


int main() {
  myClass cl1;
  myClass cl2;
  // Note that static assert can only be used if this can be known at compile time. Otherwise use
  // the boolean otherwise.
  static_assert(!does_assignment_compile<cl1, cl2>);
}

The code is available on Compiler Explorer.

Use cases

I use this for template metaprogramming in order to make sure that the code complies with certain theoretical constraints.

Sebbie answered 26/3, 2020 at 10:8 Comment(0)
C
4

try-catch happens at runtime, whereas the compiler statically tries to link functions you are calling at compile time, so compilation will always fail.

Alternatively, If you are willing to use C++ exceptions, then you could just implement the copy and assignment methods, make them public, and just throw an exception in the body of those functions. Note that in basically every situation, you should prefer static/compile-time checks over runtime checks if you have a choice.

Cargile answered 29/3, 2012 at 0:50 Comment(1)
Thanks Preet, I did this and it helped me to understand exceptions a bit more. I understand now that compile-time checks are much preferable.Burro
L
3

What you really want to test is not the compiler failing, but you want to test certain assumptions about your class.

In your test file, put #include <type_traits>

and then add

assert((std::is_assignable <myClass, myClass> ::value) == FALSE);
assert((std::is_copy_assignable<myClass> ::value) == FALSE);
assert((std::is_copy_constructible<myClass> ::value) == FALSE);

The various traits you can check for are documented here: http://en.cppreference.com/w/cpp/types

Notice, you'll have to compile for C++11 to use most of these functions.

(as described first in Assert that code does NOT compile)

Lashondra answered 3/11, 2015 at 16:17 Comment(1)
Even better would be to use static_assert instead of assert, so that it would trigger a compilation error.Dermatosis
W
0

This kind of compile errors can't be supressed. They are errors from the C++ standarts' point of view.

Surely you can supress some of them in your own (or patched) compiler.

Wideeyed answered 29/3, 2012 at 0:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.