I am implementing a device_vector in Cuda and i am taking ideas from the well known library Thust.
Now for accessing and modifying an element in that device_vector (v), I need to do v[N] = x. For that i need to overload the [] operator.
This is the code used to overload the [] operator :
T& operator[] (unsigned int index)
{
if (index >= numEle)
return ptr[0];
else
return ptr[index];
}
The problem is : To modify any memory location in Device Memory, we need to make a Cuda Kernel Call and a Cuda kernel call cannot return anything.
As far the [] overloading is concerned it returns the reference to the element we want to modify.
How can we do this for a Cuda Kernel ?
Note : I know Thrust Library somehow does this but I am not able to understand how.
The comments have very good pointers, but as an example, you can create an object that will allow you to use the [] operator to write to the CUDA array directly (or do any other things you choose):
struct CudaVector {
unsigned int get(unsigned int index) {
cout << "Get from device: " << index << endl;
return 0; // TODO read actual value
}
void set(unsigned int index, unsigned int value) {
cout << "Set in device: " << index << " " << value << endl;
// TODO write actual value
}
struct Item {
CudaVector& vector;
unsigned int index;
operator unsigned int() const {
return vector.get(index);
}
unsigned int operator=(unsigned int other) {
vector.set(index, other);
return other;
}
unsigned int operator=(const Item& other) {
return (*this = static_cast<unsigned int>(other));
}
};
Item operator[](unsigned int index) {
return Item{*this, index};
}
};
This works like:
CudaVector vector;
unsigned int foo = vector[8];
vector[5] = vector[6] = vector[7];
Output:
Get from device: 8
Get from device: 7
Set in device: 6 0
Set in device: 5 0
Idea is that your operator[] doesn't return a reference, but instead it returns a temporary object that is able to handle 'reads' (using the conversion operator) and 'writes' (using the assignment operator).
(The second overload is there to allow chained assignments, since the first one won't be picked up automatically if you don't assign from unsigned int first.)
Related
I'm trying to implement Natural numbers with C++, here's the code I have so far (O() is the 0 (zero) number and S(Nat) is the successor function).
// Peano arithmetic C++
// Null element
struct O{
explicit operator const unsigned int() const { return 0; }
};
// Successor function
struct S {
unsigned int val;
explicit operator const unsigned int() const {
// Boundary check
std::cout << "Incremented once" << std::endl;
if (this->val < UINT_MAX) {
return this->val + 1;
}
// Don't wrap around, hit the ceiling and stay there
return this->val;
}
S(const unsigned int a) {
this->val = a;
}
// Constructor
S(const O& zero) {
this->val = 0;
}
S(const S& s) {
this->val = static_cast<const unsigned int>(s);
}
};
// Nat
using Nat = std::variant<O, S>;
int main() {
std::cout << static_cast<const unsigned int>(S(O())) << std::endl;
std::cout << static_cast<const unsigned int>(S(S(O()))) << std::endl;
return 0;
}
What I expected was the static_cast to unsigned to give me 1, 2. What I get is actually 1, 1!
S(const S& s) is a copy constructor, and the compiler is allowed to elide calls to the copy constructor in certain cases. This is an optional optimization in C++14 and below, and required in C++17. You can verify that this happens here by putting a print statement inside this constructor, which won't print anything.
In this case, it will make the expression S(S(O())) equivalent to S(O()).
Therefore, this way of doing things won't work here. You can make S a function instead, which can return either an integer (making it trivial), or some object if you prefer to keep things similar to your code here.
..
..
..
const int sizes = 50;
template<class T>
class List {
private:
int curSize;
T arr[sizes];
public:
List<T>(){
cout << "constructor called\n";
this->curSize = 0;
}
void add(T element) {
arr[curSize] = element;
this->curSize++;
}
..
..
..
T operator[](int i){
if( i > sizes ){
cout << "Index out of bounds" << endl;
return arr[0];
}
return arr[i];
}
when i call the add function the operator overloading isnt working for me, only when i try to acces it from the main.cpp it works.
how can i acces the operator inside the class?
i searched here and i found a soulotion that didnt worked for me (*this).
The solution you found using (*this) was correct but your operator[] returns the wrong type so there would be no correct way to use it. Change the return from T to T&:
T& operator[](int i){
if( i > sizes || i<0 ){
cout << "Index out of bounds" << endl;
return arr[0];
}
return arr[i];
}
Then you can use it inside your class with:
(*this)[curSize] = element;
You should also have a const version:
T const& operator[](int i) const {
if( i > sizes || i<0 ){
cout << "Index out of bounds" << endl;
return arr[0];
}
return arr[i];
}
An alternate way to code the const and non const versions (to avoid the duplicated code) would be for one to delegate to the other using const_cast.
Also notice the need for checking i<0. That is a reason it would have been better for both i and sizes to be unsigned to avoid the extra check. The optimizer should fix the apparent inefficiency of the extra check, even if you leave the type signed. But the extra check still clutters the source code and forgetting it (as you did) is still an easy mistake. So using int for values which are never correctly negative is bad practice.
I am trying to overload the insertion operator '<<' to simplify some syntax required to use a specific piece of software. That software implements a hash object which holds a variety of types of data, so type checking cannot be done at compile-time because the type of the RHS of a given expression is not known until run-time. This hash is very similar in spirit to Boost Property Trees.
I am trying to write this as a template function to extract data from the hash. This works just fine as long as the receiving variable already exists (is initialized). It fails to compile if this is used during variable initialization.
So, this compiles and works fine.
int value;
value << data;
But this does not compile at all.
int value << data;
The real code is quite large and complex so I have written the following simplified program to exhibit the same behaviors.
I am using gcc version 4.3.4. A different compiler is not an option.
Any and all help is appreciated.
#include <iostream>
/**
* Simple class to use with the templates.
*/
class Data
{
public:
Data ()
{
m_value = 0;
}
Data (int val)
{
m_value = val;
}
~Data ()
{
}
int value ()
{
return (m_value);
}
int value (int val)
{
m_value = val;
return (value ());
}
private:
int m_value;
};
/**
* Assign data from RHS to LHS.
*/
template <class T>
void operator<< (T &data, Data &node)
{
data = node.value ();
}
/**
* Simple test program.
*/
int main (int argc, char *argv[])
{
// initialize the data
Data data (123);
std::cout << data.value () << std::endl;
// extract the data and assign to integer AFTER initialization
int value;
value << data;
std::cout << value << std::endl;
// extract the data and assign to integer DURING initialization
// *** problem is here ***
int other << data; // <-- this fails to compile with...
// expected initializer before '<<' token
std::cout << other << std::endl;
return (0);
}
int value << data; does not make grammatical sense.
Think of << as being like any other operator, rather like +=. Yes, << is overloaded but it still has to obey the same semantics as its natural incarnation as a bitwise shift.
int value += 3; for example, makes no sense either.
int value << data; is not valid C++.
I have a class that I use to keep track of the values assumed by a variable. I implemented it by overloading operator=.
Usage example:
myType var0;
var0 = 1;
var0 = 3;
generates on stdout:
1
3
This works fine with variables, but not with arrays. How can I extend this feature?
One way would be overloading the [] operator to return a "proxy" - an object that references your variable, and overloads the = operator to do the tracking.
Here is a sample implementation:
#include <iostream>
using namespace std;
struct myArray;
class proxy {
myArray &array;
int index;
public:
proxy(myArray &_array, int _index)
: array(_array)
, index(_index) {
}
proxy& operator=(int value);
operator int() const;
};
struct myArray {
int data[100];
proxy operator[](int index) {
return proxy(*this, index);
}
};
proxy& proxy::operator=(int value) {
cout << "Asigning " << value << " to element " << index << endl;
array.data[index] = value;
return *this;
}
proxy::operator int() const {
cout << "Reading element at " << index << endl;
array.data[index];
}
int main() {
myArray a;
a[5] = 123;
a[8] = 321;
int x = a[5];
return 0;
}
This prints
Asigning 123 to element 5
Asigning 321 to element 8
Reading element at 5
What you want to do is to use a proxy class for your array and on that class define an operator[] function. Much like how std::vector does it.
You would trace when a non-const reference is produced to an array element. I think that you'd have to assume it was about to be written to. You would make the array out of your existing class so that you'd see the actual write.
The output might look like:
ref to element 32: write 1
Or however you would like it.
Well, arrays don't support operator= anyway, so that is not a real a problem. You can assign to individual array elements of course, but that's already covered by your existing operator=.
You could create a MyArrayType that overloads the [] operator to store the values internally within an array of MyTypes
How do I change the overloaded operator to return a value instead pf a reference?
#include <iostream>
using namespace std;
class IntList
{
private:
int list[1];
public:
IntList() {list[0] = 0;}
int& operator[] (const int index) {return list[index];}
};
int main()
{
IntList list;
cout << list[0] << endl;
list[0] = 1;
cout << list[0] << endl;
return 0;
}
int operator[] (const int index){}
^^^^^
Just remove the &. Once you do that you cannot use it for assigning values to the array elements.
Difference between returning a reference and a non reference
As you noticed when operator [] returns a reference, it can be used on the left hand side of the assignment. This is possible because when you return by reference the return value of operator [] is an l-value. References are treated as l-values because you can take reference of variables that are stored in memory and have an address.
When operator [] returns by value the expression list[0] = 1; will eventually evaluate[#] to something like,
1=1;
Which is illogical, since 1 is not a l-value, the compiler will generate a diagnostic that the left operand must be a l-value.
[#] Assuming value of the element at subscript 0 is 1:
You can do this by just removing the &, so you have
int operator[] (const int index){}.
However as you noticed then the problem is that you cannot assign to it without a compilation error because the index operator no longer returns an l-value. So I think you should consider why you want to return a value instead of a reference. It is possible that you want a pattern where the index operator cannot be use to assign to the object, maybe for some sort of read only type object. Your other option is to have a separate function to set it because the index operator can no longer be used to do that
In your code example you are using assignment, which requires that you return a reference.
list[0] = 1;
list.operator[](0) = 1;
int& xref = list.operator[](0);
(xref) = 1; // <-- changed the value of list element 0.
Given that you want operator[](int index) to return a value, this would translate to:
int x = list.operator[](0);
x = 1; <-- you changed x, not list[0].
If you want operator[](int index) to return a value but also have list[0] = 1 still work, you're going to need to provide two versions of the operator so that the compiler can determine which behavior you are trying to invoke in a given call:
// const member, returns a value.
int operator[] (const int index) const {return list[index];}
// non const member, which returns a reference to allow n[i] = x;
int& operator[] (const int index) {return list[index];}
Note that they must differ by both return type AND member-constness.
#include <iostream>
using namespace std;
class IntList
{
private:
int list[1];
public:
IntList() {list[0] = 0;}
int operator[] (const int index) const { return list[index]; }
int& operator[] (const int index) {return list[index];}
};
int main(int argc, const char** argv)
{
IntList list;
cout << list[0] << endl;
list[0] = 1;
int x = list[0];
cout << list[0] << ", " << x << endl;
return 0;
}
Working demo: http://ideone.com/9UEJND