I'm trying to create a wrapper around JsonCpp's Json::Value
.
I have a namespace global
where I have functions that work on Json::Value&
parameters. I want to create a more syntactically pleasing wrapper class for those functions.
This is a minimal example.
// Impl is a typedef for Json::Value, from JsonCpp
namespace global
{
Impl& getChild(Impl& mImpl, const std::string& mName) { return mImpl[mName]; }
const Impl& getChildConst(const Impl& mImpl, const std::string& mName) { return mImpl[mName]; }
Impl::iterator beginNonConst(Impl& mRoot) { return mRoot.begin(); }
Impl::iterator endNonConst(Impl& mRoot) { return mRoot.end(); }
Impl::const_iterator beginConst(const Impl& mRoot) { return mRoot.begin(); }
Impl::const_iterator endConst(const Impl& mRoot) { return mRoot.end(); }
}
class Wrapper
{
private:
Impl& impl;
public:
Wrapper(Impl& mImpl) : impl(mImpl) { }
Wrapper operator[](const std::string& mName) { return global::getChild(impl, mName); }
// Error here
// no known conversion for argument 1 from 'const Impl {aka const Json::Value}' to 'Impl& {aka Json::Value&}'
const Wrapper operator[](const std::string& mName) const { return global::getChildConst(impl, mName); }
Impl::iterator begin() { return global::beginNonConst(impl); }
Impl::iterator end() { return global::endNonConst(impl); }
Impl::const_iterator begin() const { return global::beginConst(impl); }
Impl::const_iterator end() const { return global::endConst(impl); }
};
This is what I want to be able to compile:
int main()
{
Json::Value realValue;
Wrapper w(realValue)
for(const auto& i : w["numberArray"]) { cout << i << endl; }
for(auto& i : w["numberArray"]) { i += 100; }
// assert that changes have been made to realValue through the wrapper
return 0;
}
A Wrapper
has a member of type Impl&
. Making the Wrapper
object const
only changes the top-level const
of its members (which does nothing on a reference, references are already non-rebindable), and even that takes effect only after the constructor finishes.
You need a class with a member of type const Impl&
, and const Wrapper
does not. The compiler is correctly preventing you from losing the const
qualifier and passing a const Impl&
to Wrapper::Wrapper(Impl&)
which could mutate its argument.
Generally const_iterator
is a separate class from iterator
. I see no reason why your Wrapper
would be any different.
As a short-term solution, you can just use
const Wrapper operator[](const std::string& mName) const { return global::getChild(impl, mName); }
but this doesn't prevent anyone from copying the return value into a non-const Wrapper
object and using it to mutate the Impl
.