msdn link
text here:
'function call' : recursive call has no side effects, deleting A
function contains a recursive call, but otherwise has no side effects.
A call to this function is being deleted. The correctness of the
program is not affected, but the behavior is. Whereas leaving the call
in could result in a runtime stack overflow exception, deleting the
call removes that possibility.
The code causing this warning is:
template<class Key, class Value>
void Map<Key, Value>::Clear(NodeType* pNode)
{
((Key*) (pNode->m_key))->~Key();
((Value*) (pNode->m_item))->~Value();
NodeType* pL = pNode->GetLeftChild();
NodeType* pR = pNode->GetRightChild();
if (pL != &m_dummy)
{
Clear(pL);
}
if (pR != &m_dummy)
{
Clear(pR);
}
}
and 1 more point: this warning only happens in release build (/Ox)
What is this warning? Thanks!
I'll bet it occurs when ~Key and ~Value are no-ops. The compiler will notice that there is literally nothing else that this function tries to do, so it entirely eliminates this function.
For starters, the warning only happens in a release build because it's the result of an optimization, and the optimizer only runs during release builds.
The optimizer is allowed to restructure or eliminate code, including recursive calls, if it can prove that that will not change the program behavior. There is probably some data-dependent path where one or both calls to Clear() for the left and right nodes would have no effect.
Edit: As #MSalters points out, it is more likely that the destructors for Key and Value are no-ops -- as they would be if Key and Value are both plain-old-data structures or simple types, since the destructors are the only side-effect possible from the function as written.
Edit 2: instead of using placement new, why not use the : initializer?
template struct Map<typename Key, typename Value> {
Key key;
Value value;
Map(Key k, Value v) : key(k), value(v) {}
}
Related
I'm a bit confused about isLandingPad on BasicBlocks in LLVM. I have the following code, where I create an empty BasicBlock and then call isLandingPad on it:
#include "llvm/IR/IRBuilder.h"
#include <assert.h>
using namespace llvm;
int main(void)
{
// Start with a LLVM context.
LLVMContext TheContext;
// Make a module.
Module *TheModule = new Module("mymod", TheContext);
// Make a function
std::vector<Type*> NoArgs = {};
Type *u32 = Type::getInt32Ty(TheContext);
FunctionType *FT = FunctionType::get(u32, NoArgs, false);
Function *F = Function::Create(FT, Function::ExternalLinkage, "main", TheModule);
// Make an empty block
IRBuilder<> Builder(TheContext);
BasicBlock *BB = BasicBlock::Create(TheContext, "entry", F);
Builder.SetInsertPoint(BB);
auto fnp = BB->getFirstNonPHI();
assert(fnp == nullptr);
// I think this should crash.
auto islp = BB->isLandingPad();
printf("isLP = %d\n", islp);
// If we inline the implementation of the above call, we have the following
// (which *does* crash).
auto islp2 = isa<LandingPadInst>(BB->getFirstNonPHI());
printf("isLP2 = %d\n", islp2);
return 0;
}
which outputs:
isLP = 0
codegen: /usr/lib/llvm-7/include/llvm/Support/Casting.h:106: static bool llvm::isa_impl_cl<llvm::LandingPadInst, const llvm::Instruction *>::doit(const From *) [To = llvm::LandingPadInst, From = const llvm::Instruction *]: Assertion `Val && "isa<> used on a null pointer"' failed.
According to the LLVM source of isLandingPad (https://llvm.org/doxygen/BasicBlock_8cpp_source.html#l00470) this should segfault when the BasicBlock is empty (since we are calling isa on a nullptr). However, when I run this program the call to isLandingPad succeeds and returns false. Interestingly, when I inline the function definition of isLandingPad (as seen further below), it crashes as expected.
I'm clearly doing something wrong here, but I don't see in what way the BB->isLandingPad() call is different to the inlined version, and why isLandingPad doesn't crash, when it should according to the source.
LLVM itself is (at least on my system) compiled with assertions disabled, so the assertion doesn't trigger. When you inline it in your code, you are compiling with assertions enabled, so it does trigger.
Note that since isa<...> is a template, it will be compiled into the compilation unit it is instantiated as part of. In this case, there's at least two: one in LLVM and one that comprises your program. Strictly speaking they should both be identical (the "one definition rule") or you have UB anyway. The practical upshot in a case like this one is that calls to isa<...>() from either compilation unit might end up calling the version instantiated in the other one. However, it's likely that in the case of isa<...>() the calls are being inlined, i.e. you end up with a version of isa<...>() specific to each compilation unit that instantiates it.
If the code "should segfault", that seems to imply that the code is invoking undefined behavior (UB) at runtime. It is a possibility that the compiler is doing optimizations based on the false assumption that UB does not occur in your program and this false assumption leads to the false result isLP == false that you observe.
You should never invoke undefined behavior and restructure your code to never call functions with parameters that can call UB. (E.g., check the result of getFirstNonPHI before calling isa<LandingPadInst> or isLandingPad.
Specifically you should not assume that UB (such as dereferencing nullptr or an address near it) has a well-defined effect such as "it will segfault" because the compiler may reorganize your code (assuming UB never happens) in ways that will eliminate the effect you expect (e.g., it will generate code that doesn't attempt to load from nullptr).
Inlining and optimization levels have great effect on the generated code and this is why you see different results (invalid return value vs. segfault) in different cases.
More info on undefined behavior:
Undefined, unspecified and implementation-defined behavior
What Every C Programmer Should Know About Undefined Behavior
https://en.cppreference.com/w/cpp/language/ub (See the links at the bottom of the page for further references)
Given a setup like this, where DoFooStuff() is called:
class Foo {
public:
void DoFooStuff(); // calls Bar::DoBarStuff()
}
class Bar {
public:
void DoBarStuff(); // Calls Bar::DoInternalBarStuff()
protected:
void DoInternalBarStuff();
}
What could make it possible that my stack trace could show exactly this?:
Type Function
void Bar::DoInternalBarStuff()
void Foo::DoFooStuff()
The only reference to DoInternalBarStuff() is in DoBarStuff(). DoInternalBarStuff() asserts on it's first line:
assert(false);
And that is the location where the stack trace is taken from.
Is the call to Bar::DoBarInternalStuff the last statement in Bar::DoBarStuff? If so, the compiler most likely replaced Bar::DoBarStuff's stack frame with Bar::DoBarInternalStuff's when Bar::DoBarInternalStuff was called.
This kind of tail call optimization is fairly common in C/C++ compilers. It reduces the stack depth required when a recursive function can be arranged such that the recursive call is the last call in the function.
So it turns out that the compiler does automatic inlining at certain optimization levels. Learn new things every day. :)
Does GCC inline C++ functions without the 'inline' keyword?
(Thanks for the helpful comments that showed me this.)
So I ran across this (IMHO) very nice idea of using a composite structure of a return value and an exception - Expected<T>. It overcomes many shortcomings of the traditional methods of error handling (exceptions, error codes).
See the Andrei Alexandrescu's talk (Systematic Error Handling in C++) and its slides.
The exceptions and error codes have basically the same usage scenarios with functions that return something and the ones that don't. Expected<T>, on the other hand, seems to be targeted only at functions that return values.
So, my questions are:
Have any of you tried Expected<T> in practice?
How would you apply this idiom to functions returning nothing (that is, void functions)?
Update:
I guess I should clarify my question. The Expected<void> specialization makes sense, but I'm more interested in how it would be used - the consistent usage idiom. The implementation itself is secondary (and easy).
For example, Alexandrescu gives this example (a bit edited):
string s = readline();
auto x = parseInt(s).get(); // throw on error
auto y = parseInt(s); // won’t throw
if (!y.valid()) {
// ...
}
This code is "clean" in a way that it just flows naturally. We need the value - we get it. However, with expected<void> one would have to capture the returned variable and perform some operation on it (like .throwIfError() or something), which is not as elegant. And obviously, .get() doesn't make sense with void.
So, what would your code look like if you had another function, say toUpper(s), which modifies the string in-place and has no return value?
Have any of you tried Expected; in practice?
It's quite natural, I used it even before I saw this talk.
How would you apply this idiom to functions returning nothing (that is, void functions)?
The form presented in the slides has some subtle implications:
The exception is bound to the value.
It's ok to handle the exception as you wish.
If the value ignored for some reasons, the exception is suppressed.
This does not hold if you have expected<void>, because since nobody is interested in the void value the exception is always ignored. I would force this as I would force reading from expected<T> in Alexandrescus class, with assertions and an explicit suppress member function. Rethrowing the exception from the destructor is not allowed for good reasons, so it has to be done with assertions.
template <typename T> struct expected;
#ifdef NDEBUG // no asserts
template <> class expected<void> {
std::exception_ptr spam;
public:
template <typename E>
expected(E const& e) : spam(std::make_exception_ptr(e)) {}
expected(expected&& o) : spam(std::move(o.spam)) {}
expected() : spam() {}
bool valid() const { return !spam; }
void get() const { if (!valid()) std::rethrow_exception(spam); }
void suppress() {}
};
#else // with asserts, check if return value is checked
// if all assertions do succeed, the other code is also correct
// note: do NOT write "assert(expected.valid());"
template <> class expected<void> {
std::exception_ptr spam;
mutable std::atomic_bool read; // threadsafe
public:
template <typename E>
expected(E const& e) : spam(std::make_exception_ptr(e)), read(false) {}
expected(expected&& o) : spam(std::move(o.spam)), read(o.read.load()) {}
expected() : spam(), read(false) {}
bool valid() const { read=true; return !spam; }
void get() const { if (!valid()) std::rethrow_exception(spam); }
void suppress() { read=true; }
~expected() { assert(read); }
};
#endif
expected<void> calculate(int i)
{
if (!i) return std::invalid_argument("i must be non-null");
return {};
}
int main()
{
calculate(0).suppress(); // suppressing must be explicit
if (!calculate(1).valid())
return 1;
calculate(5); // assert fails
}
Even though it might appear new for someone focused solely on C-ish languages, to those of us who had a taste of languages supporting sum-types, it's not.
For example, in Haskell you have:
data Maybe a = Nothing | Just a
data Either a b = Left a | Right b
Where the | reads or and the first element (Nothing, Just, Left, Right) is just a "tag". Essentially sum-types are just discriminating unions.
Here, you would have Expected<T> be something like: Either T Exception with a specialization for Expected<void> which is akin to Maybe Exception.
Like Matthieu M. said, this is something relatively new to C++, but nothing new for many functional languages.
I would like to add my 2 cents here: part of the difficulties and differences are can be found, in my opinion, in the "procedural vs. functional" approach. And I would like to use Scala (because I am familiar both with Scala and C++, and I feel it has a facility (Option) which is closer to Expected<T>) to illustrate this distinction.
In Scala you have Option[T], which is either Some(t) or None.
In particular, it is also possible to have Option[Unit], which is morally equivalent to Expected<void>.
In Scala, the usage pattern is very similar and built around 2 functions: isDefined() and get(). But it also have a "map()" function.
I like to think of "map" as the functional equivalent of "isDefined + get":
if (opt.isDefined)
opt.get.doSomething
becomes
val res = opt.map(t => t.doSomething)
"propagating" the option to the result
I think that here, in this functional style of using and composing options, lies the answer to your question:
So, what would your code look like if you had another function, say toUpper(s), which modifies the string in-place and has no return value?
Personally, I would NOT modify the string in place, or at least I will not return nothing. I see Expected<T> as a "functional" concept, that need a functional pattern to work well: toUpper(s) would need to either return a new string, or return itself after modification:
auto s = toUpper(s);
s.get(); ...
or, with a Scala-like map
val finalS = toUpper(s).map(upperS => upperS.someOtherManipulation)
if you don't want to follow a functional route, you can just use isDefined/valid and write your code in a more procedural way:
auto s = toUpper(s);
if (s.valid())
....
If you follow this route (maybe because you need to), there is a "void vs. unit" point to make: historically, void was not considered a type, but "no type" (void foo() was considered alike a Pascal procedure). Unit (as used in functional languages) is more seen as a type meaning "a computation". So returning a Option[Unit] does make more sense, being see as "a computation that optionally did something". And in Expected<void>, void assumes a similar meaning: a computation that, when it does work as intended (where there are no exceptional cases), just ends (returning nothing). At least, IMO!
So, using Expected or Option[Unit] could be seen as computations that maybe produced a result, or maybe not. Chaining them will prove it difficult:
auto c1 = doSomething(s); //do something on s, either succeed or fail
if (c1.valid()) {
auto c2 = doSomethingElse(s); //do something on s, either succeed or fail
if (c2.valid()) {
...
Not very clean.
Map in Scala makes it a little bit cleaner
doSomething(s) //do something on s, either succeed or fail
.map(_ => doSomethingElse(s) //do something on s, either succeed or fail
.map(_ => ...)
Which is better, but still far from ideal. Here, the Maybe monad clearly wins... but that's another story..
I've been pondering the same question since I've watched this video. And so far I didn't find any convincing argument for having Expected, for me it looks ridiculous and against clarity&cleanness. I have come up with the following so far:
Expected is good since it has either value or exceptions, we not forced to use try{}catch() for every function which is throwable. So use it for every throwing function which has return value
Every function that doesn't throw should be marked with noexcept. Every.
Every function that returns nothing and not marked as noexcept should be wrapped by try{}catch{}
If those statements hold then we have self-documented easy to use interfaces with only one drawback: we don't know what exceptions could be thrown without peeking into implementation details.
Expected impose some overheads to the code since if you have some exception in the guts of your class implementation(e.g. deep inside private methods) then you should catch it in your interface method and return Expected. While I think it is quite tolerable for the methods which have a notion for returning something I believe it brings mess and clutter to the methods which by design have no return value. Besides for me it is quite unnatural to return thing from something that is not supposed to return anything.
It should be handled with compiler diagnostics. Many compilers already emit warning diagnostics based on expected usages of certain standard library constructs. They should issue a warning for ignoring an expected<void>.
Say I have the following function:
Thingy& getThingy(int id)
{
for ( int i = 0; i < something(); ++i )
{
// normal execution guarantees that the Thingy we're looking for exists
if ( thingyArray[i].id == id )
return thingyArray[i];
}
// If we got this far, then something went horribly wrong and we can't recover.
// This function terminates the program.
fatalError("The sky is falling!");
// Execution will never reach this point.
}
Compilers will typically complain at this, saying that "not all control paths return a value". Which is technically true, but the control paths that don't return a value abort the program before the function ends, and are therefore semantically correct. Is there a way to tell the compiler (VS2010 in my case, but I'm curious about others as well) that a certain control path is to be ignored for the purposes of this check, without suppressing the warning completely or returning a nonsensical dummy value at the end of the function?
You can annotate the function fatalError (its declaration) to let the compiler know it will never return.
In C++11, this would be something like:
[[noreturn]] void fatalError(std::string const&);
Pre C++11, you have compiler specific attributes, such as GCC's:
void fatalError(std::string const&) __attribute__((noreturn));
or Visual Studio's:
__declspec(noreturn) void fatalError(std::string const&);
Why don't you throw an exception? That would solve the problem and it would force the calling method to deal with the exception.
If you did manage to haggle the warning out some way or other, you are still left with having to do something with the function that calls getThingy(). What happens when getThingy() fails? How will the caller know? What you have here is an exception (conceptually) and your design should reflect that.
You can use a run time assertion in lieu of your fatalError routine. This would just look like:
Thingy& getThingy(int id)
{
for ( int i = 0; i < something(); ++i )
{
if ( thingyArray[i].id == id )
return thingyArray[i];
}
// Clean up and error condition reporting go here.
assert(false);
}
I have to return to the previous level of the recursion. is the syntax like below right?
void f()
{
// some code here
//
return;
}
Yes, you can return from a void function.
Interestingly, you can also return void from a void function. For example:
void foo()
{
return void();
}
As expected, this is the same as a plain return;. It may seem esoteric, but the reason is for template consistency:
template<class T>
T default_value()
{
return T();
}
Here, default_value returns a default-constructed object of type T, and because of the ability to return void, it works even when T = void.
Sure. You just shouldn't be returning an actual value.
Yes, you can use that code to return from the function. (I have to be very verbose here to make Stack Overflow not say that my answer is too short)
Yes, that will return from the function to the previous level of recursion. This is going to be very basic explanation, but when you call a function you are creating a new call stack. In a recursive function you are simply adding to that call stack. By returning from a function, whether you return a value or not, you are moving the stack pointer back to the previous function on the stack. It's sort of like a stack of plates. You keep putting plates on it, but than returning moves the top plate.
You could also verify this by using a debugger. Just put a few break points in your code and step through it. You can verify yourself that it works.
The simple answer to this is YES! C++ recognise void method as a function with no return. It basically tells the compiler that whatever happens, once you see the return; break and leave the method....
Yes, sometimes you may wish to return void() instead of just nothing.
Consider a void function that wants to call some pass-through void functions without a bunch of if-else.
return
InputEvent == E_Pressed ? Controller->Grip() :
InputEvent == E_Released ? Controller->Release() :
InputEvent == E_Touched ? Controller->Touch() : void();
You shouldn't have to have the return there, the program will return to the previous function by itself, go into debug mode and step through and you can see it yourself.
On the other hand i don't think having a return there will harm the program at all.
As everyone else said, yes you can. In this example, return is not necessary and questionably serves a purpose. I think what you are referring to is an early return in the middle of a function. You can do that too however it is bad programming practice because it leads to complicated control flow (not single-entry single-exit), along with statements like break. Instead, just skip over the remainder of the function using conditionals like if/else().