-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
assign and bind to deal with different const of functions #118
Conversation
@ywwu928 please make sure this doesn't do extra stores. |
|
Would it be beneficial to introduce a wrapper around references to arrays that has a const ref, but uses template <typename T>
class ConstVecWrapper {
public:
ConstVecWrapper(std::vector<T>& input) : ref(input) {}
T& operator[](size_t idx) {
return const_cast<std::vector<T>&>(ref)[idx];
}
private:
const std::vector<T>& ref;
} Using a wrapper like this would allow the compiler to prevent any mutation of the input reference, and you'd use |
I think that approach makes sense. However, the goal is to completely reduce the burden on the programmer for the provided methods. If the programmer has to keep track of that how is it different from knowing when to use applyFunc? |
The idea is that by having some kind of class like this wrap the reference to the array most users don't need to know about how the const semantics are handled internally - they just have a reference that only exposes interior mutability. I think the risk with |
Correct this would loose the internal mutations. How about you write out an example use case of what you are saying and we examine that. |
I don't think this should replace // let's say we have a function f that attempts to do something that mutates the ptr itself
void f(GlobalRef<T> ref) {
ref->size += 1; // contrived example, but whatever
}
// If I call the applyFunc with f, my understanding is that the write to ref->size won't persist
// what if applyFunc was defined as follows:
void applyFunc(ConstWrapper<T> ref, Func f) {
...
}
// When calling applyFunc if I just did...
GlobalRef<T> ref;
ConstVecWrapper<T> mut_ref(ref); // (this isn't necessary I think you can make it implicit cast)
applyFunc(ref, f); // ...I'll get a compiler error about the type of f
// If I just naively refactor this to...
void f(ConstWrapper<T> ref) {
ref->size += 1; // ...now this will fail to compile because `ref->size` will have type `const size_t&`
// Only operator[] allows mutable access
// now I know that f shouldn't be called via `applyFunc` but with `bindFunc`/`fmap`
} |
Separate Idea: At that point why not just have two impls of bind. One with a ConstWrapper and one without? Let's consider a more involved example. Because I think what you are saying is a good idea. template<typename VT, typename ET>
struct CSR {
Array<uint64_t> edgeIndexes;
Array<uint64_t> edgeDsts;
Array<VT> vertexData;
Array<ET> edgeData;
uint64_t numVertices;
uint64_t numEdges;
}; Very obviously when accessing this data structure certain things (for example those wrapped in arrays) can be made mutable without any cost. Perhaps we can implicitly promote things like Arrays and other non-resizeable DS as automatically It would be much nicer to be able to do this with two overloaded versions of bind: void f(const CSR& graph) {
graph.vertexData[0] = 5;
}
void g(CSR graph) {
graph.numVertices ++;
}
globalRef<CSR> csr = distGraph.getLocalCSR();
bind(csr, f); // No writeback
bind(csr, g); // Yes writeback I think what we are saying is the same, just instead of using mutable, you want to use an explicit separate type. |
Thar definitely makes sense, but it wouldn't work for the case where we want to be able to mutate array elements right? |
c3bf573
to
0c38f0f
Compare
We will merge this for now and I'll update on llvm patches in today's meeting. |
assign is used when the function is const wrt to the first level of the referenced object.
bind is it's counterpart.
Both should only be used in very isolated cases.