Is there a way to check if certain variable is initialized before some point in a program?
For example, how to check if certain variable is initialized somewhere before the IfStmt node?
Methods from VarDecl class (hasInit() and getInit()) are not enough because of the following situation:
int x = 0; // hasInit() return true
int y;
...
y = 0; // initialized here, but hasInit() returns false
...
if (...) {}
If you maintain a product written by C++ code and hope to remove ugly indeterminate variables, a reasonable way to do it is defining an initializing function or lambda f, and then declare a local variable as const auto x = f(...); from the get-go.
OTOH, if you delay the value asignment on purpose, there are several methods to detect the value is assigned or not.
I just came up with following methods.
std::optional
In C++17 and over,
std::optional<T> enables us to detect whether values are assigned or not.
std::optional::has_value and std::optional::value correspond to your hasInit and getInit respectively as follows:
DEMO
#include <iostream>
#include <optional>
template<typename T>
void checkInitialization(const std::optional<T>& a)
{
if(a.has_value()){
std::cout << "Value is assigned by " << a.value() << "." << std::endl;
}
else{
std::cout << "Value is still not assigned." << std::endl;
}
}
int main(void)
{
std::optional<int> x;
checkInitialization(x); // Value is still not assigned
x = 1;
checkInitialization(x); // Value is assigned
return 0;
}
The output is as follows:
Value is still not assigned.
Value is assigned by 1.
std::unique_ptr
We can also check it using std::unique_ptr<T> which is introduced from C++11.
First we define a variable as std::unique_ptr<T> x; where (x == nullptr) is still true.
Later on, we assign a value by x = std::unique_ptr<int>(new int(1)) and then (x == nullptr) becomes false.
(In C++14 x = std::make_unique<int>(1) works and is simple.)
Thus we can again get the previous output with the following code:
DEMO
#include <iostream>
#include <memory>
template<typename T>
bool hasInit(const std::unique_ptr<T>& a)
{
return (a != nullptr);
}
template<typename T>
const T& getInit(const std::unique_ptr<T>& a)
{
return *a;
}
template<typename T>
void checkInitialization(const std::unique_ptr<T>& a)
{
if(hasInit(a)){
std::cout << "Value is assigned by " << getInit(a) << "." << std::endl;
}
else{
std::cout << "Value is still not assigned." << std::endl;
}
}
int main(void)
{
std::unique_ptr<int> x;
checkInitialization(x); // Uninitialized
x = std::unique_ptr<int>(new int(1));
//x = std::make_unique<int>(1); // C++14
checkInitialization(x); // Initialized
return 0;
}
std::pair
We can also apply std::pair<bool, T> where std::pair::first and std::pair::second correspond to your hasInit and getInit respectively.
We again get the previous output:
DEMO
#include <iostream>
#include <utility>
template<typename T>
void checkInitialization(const std::pair<bool, T>& a)
{
if(a.first){
std::cout << "Value is assigned by " << a.second << "." << std::endl;
}
else{
std::cout << "Value is still not assigned." << std::endl;
}
}
int main(void)
{
std::pair<bool, int> x{false, 0};
checkInitialization(x); // Uninitialized
x = {true, 1};
checkInitialization(x); // Initialized
return 0;
}
Firstly as mentioned in the comments:
int y = 0; // initialization
int y; y = 0; // assignment
Let's assume you want to detect assignment. One simple way could be wrap the integer you want to track in a struct and write a custom operator = (int). For example:
struct Foo
{
Foo() {std::cout << "default init" << std::endl;}
Foo& operator = (int elem)
{
cout<<"Int-Assignment operator called "<<endl;
x = elem;
is_assigned = true;
return *this;
}
int x = 0; // default initialized to 0
bool is_assigned = false; // default initialized to false
};
Now let's see what happens:
int main()
{
Foo t1;
// t1.is_assigned is false
t1 = 0;
// t1.is_assigned is true
return 0;
}
You could use something like this or a variant if needed. Here's the code running online corresponding to the above.
Is this what you wanted?
Related
So I had this scenario where I want to call either function of a class, where the function in question has the same prototype but it's also overloaded. Since I know of pointer to members my immediate reaction was something like this:
struct test
{
int overloaded(char) {}
int overloaded(int) {}
int overloadedone(char) {}
int overloadedone(int) {}
} test;
int main()
{
(test.*(true ? (&test::overloaded) : (&test::overloadedone)))(1);
}
However it turned out the compiler (MSVC - 2019 Preview latest version with std C++ preview) can't deduce the type and I have to write:
(test.*(true ? static_cast<int (test::*)(int)>(&test::overloaded) : static_cast<int (test::*)(int)>(&test::overloadedone)))(1);
instead which made me return to the good old:
true ? test.overloaded(1) : test.overloadedone(1);
But I wonder if this is the defined behavior of requiring those cast. Even:
(test.*static_cast<int (test::*)(int)>(true ? (&test::overloaded) : (&test::overloadedone)))(1);
Doesn't work.
You have to write said cast on each of the two possibilities for the ternary as in the second example.
It isn't particularly elegant, but this approach can deduce an overload if you curry the member function pointer's arguments before passing the member function pointers themselves:
#include <iostream>
template <class... Args>
auto invoke_conditional_mem_fn(Args... args)
{
return [=] <class R, class X> (X x, bool b, R(X::*t)(Args...), R(X::*f)(Args...)) -> R
{
return (x.*(b ? t : f))(args...);
};
}
struct test
{
int overloaded(char) { std::cout << "overloaded(char) "; return 1; }
int overloaded(int) { std::cout << "overloaded(int) "; return 2; }
int overloadedone(char) { std::cout << "overloadedone(char) "; return 3; }
int overloadedone(int) { std::cout << "overloadedone(int) "; return 4; }
} test;
int main()
{
std::cout
<< invoke_conditional_mem_fn('1')(test, true, &test::overloaded, &test::overloadedone)
<< std::endl
<< invoke_conditional_mem_fn(1)(test, false, &test::overloaded, &test::overloadedone)
<< std::endl;
}
Thanks to #dyp and their example, we know that we can infer the return type and base of the member function pointers if we select which arguments to pass.
Alternatively, you could do something a little simpler like this, if it meets your needs. Just declare a lambda to work around the limitations of your ternary expression with an if and else statement since each branch of a ternary operator is required to be of the same type.
#include <iostream>
struct test
{
int overloaded(char) { std::cout << "overloaded(char) "; return 1; }
int overloaded(int) { std::cout << "overloaded(int) "; return 2; }
int overloadedone(char) { std::cout << "overloadedone(char) "; return 3; }
int overloadedone(int) { std::cout << "overloadedone(int) "; return 4; }
} test;
auto conditional = [] (struct test& test, bool cond, auto... args)
{
if (cond) return test.overloaded(args...);
else return test.overloadedone(args...);
};
int main()
{
std::cout << conditional(test, true, '1') << std::endl;
std::cout << conditional(test, false, 1) << std::endl;
}
Take a look at this simplified version of what I'm coding and you'll understand the problem...
#include <iostream>
template<typename T, int N = 2>
class Array
{
T m_arr[N]{ 0 };
int m_size = N;
public:
T& operator[](const int& index) { return m_arr[index]; }
};
int main()
{
Array<int, 10>* thing = new Array<int, 10>();
thing[2] = 5;
std::cout << thing[2] << std::endl;
}
Now, obviously the first thing that comes to mind is, hey, just like I overloaded the [] operator, lets overload the = and the << operators, but that wouldn't work, since that is only valid for doing something like this...
thing = whatever;
std::cout << thing << std::endl;
But how could I do it for...
thing[num] = whatever;
std::cout << thing[num] << std::endl;
For indexing I use std::unordered_map and std::map. Both of them throws compiling errors when using as follow:
std::unordered_map<std::function<bool(Ent*)>, int> var;
std::unordered_map fails due referencing deleted function
std::map fails due no < operator
The ideal solution for me would be to use a type of map, but if is a must to use another type of container, then it shouldn't be a problem
One way of having functions as container key is to wrap them into functor structure
#include <unordered_map>
#include <typeinfo>
struct FunctorSum {
int operator()(int x, int y) {
return x + y;
}
};
struct FunctorMult {
int operator()(int x, int y) {
return x * y;
}
};
int main() {
std::unordered_map<size_t, int> funcToInt;
funcToInt[typeid(FunctorSum).hash_code()] = 0;
funcToInt[typeid(FunctorMult).hash_code()] = 1;
return 0;
}
Here I used typeid as hash, but it can also be hardcoded into functor struct.
Another way is to use std::function::target_type to calculate hash of the function, which will work only with lambdas. But you can always wrap any function into lambda.
#include <iostream>
#include <functional>
using FuncType = std::function<bool(int)>;
bool x(int v) { return v == 0; }
std::string hash(FuncType f) {
return f.target_type().name();
}
int main() {
auto y = [](int v) { return v == 1; };
auto z = [](int v) { return v == 2; };
std::cout << "x: " << hash(x) << std::endl;
std::cout << "y: " << hash(y) << std::endl;
std::cout << "z: " << hash(z) << std::endl;
return 0;
}
Output
x: PFbiE
y: Z4mainEUliE_
z: Z4mainEUliE0_
I was trying to make a function that assigns y to x regardless whether x, y are int or std::string. I wrote this code:
#include <iostream>
#include <string>
#include <typeinfo>
template <typename T>
T& assign(T& x, T& y){
if ( typeid(x).name() == "Ss" && typeid(y).name() == "Ss" ){
std::string k = static_cast<std::string>(y);
x = k;
return x;
}
else if ( typeid(x).name() == "i" && typeid(y).name() == "i" ){
int k = static_cast<int>(y);
x = k;
return x;
}
else{
std::cout << "uncorrect assignment" << std::endl;
}
}
int main(){
std::string a = "empty_string";
std::string b = "Hi there";
assign(a, b);
std::cout << a << std::endl;
}
But it doesn’t work.
It gives the error:
[Error] invalid static_cast from type ‘std::basic_string<char>’ to type
at line 14:
int k = static_cast<int>(y);
I can’t understand, what is the problem?
I know the objection: I might have just defined function assign as:
template <typename T>
T& assign(T& x, T& y){
x = y;
}
which works. However, I was working on an other more complex function on which I have to (or at least I haven’t found any way other than) use static_cast.
So, if you could, please, explain to me what is the mistake in this example, I may try to fix the function I am working on.
Thank you very much,
Simone.
To do what do you want, you need C++17 and if constexpr. And the use of something that works compile-time, not of typeid that works runtime.
The problem is that with your code, typeid permit, runtime, to choose the if or the else part of your code, but the compiler must compile both part. So must compile
int k = static_cast<int>(y);
x = k;
when T is std::string. This give an error.
You need a type-traits (std::is_same, by example), that is evaluated compile-time, and a construct that avoid the compilation of the wrong part. This construct is if constexpr ( <test> ) (where the <test> is valuable compile time) but, unfortunately, is available only from C++17.
So, in C++17 you can write
template <typename T>
void assign (T & x, T const & y)
{
if constexpr ( std::is_same<T, std::string>::value ) {
std::string k = static_cast<std::string>(y);
x = k;
}
else if constexpr ( std::is_same<T, int>::value ) {
int k = static_cast<int>(y);
x = k;
}
else {
std::cout << "uncorrect assignment" << std::endl;
}
}
but, pre C++17, you have to follows different ways.
To handle different types separately inside a function, an option is to define a local struct with overloaded function call operators to different types:
#include <iostream>
#include <string>
template<typename T>
T& assign(T& x, const T& y) {
struct {
void operator()(std::string& lhs, const std::string& rhs) {
std::cout << "Type is std::string." << std::endl;
lhs = rhs;
}
void operator()(int& lhs, const int& rhs) {
std::cout << "Type is int." << std::endl;
lhs = rhs;
}
} assign_impl;
assign_impl(x, y);
return x;
}
int main() {
/* Test No. 1 */ {
std::string dest, src = "Foo";
std::cout << "Test result: " << assign(dest, src) << std::endl;
}
/* Test No. 2 */ {
int dest, src = 32;
std::cout << "Test result: " << assign(dest, src) << std::endl;
}
}
The code above will work on C++98 and above but its disadvantage is that it will raise compiler errors if you try to use it with unhandled types.
#include <iostream>
typedef std::function<bool(int)> set;
using namespace std;
set singletonSet(int a) {
return [&] (int x) { return (a == x); };
}
bool contains(set s, int test) {
return s(test);
}
int main() {
auto first = singletonSet(5);
auto r1 = contains(first, 10);
auto r2 = contains(first, 5);
cout << r1 << " " << r2 << endl;
return 0;
}
I expect this to print 0 1 but result is 1 1
Beginner c++ labmda programmer here so sorry in advance if this is a basic error.
Don't capture a by reference, capture it by value. As-is, you're storing a dangling reference inside the lambda, and using it later leads to UB.
set singletonSet(int a) {
return [=] (int x) { return (a == x); };
// ^
}