Work around lacking const-correctness of external library

164 views Asked by At

I am using an external C++ library that lacks const-correctness. Lets say I am working with objects of the following class:

// Library.h
namespace Library {

class Message {
 public:
    std::string getData() {
        return data_;
    }
 private:
    std::string data_;
};

}  // namespace Library

Note that getData() returns a copy, thus a call to the method does not change the Message object and it should be const. However, the vendor decided it's not. On my side of the code, const-correctness is important and the Message is to be used in a function like so:

// MyApplication.cpp

template<class T>
void handleMessage(const T& msg) {
    std::string content = msg.getData();
    // interprete and process content ...
}

Is there a way to achieve this? In other words, how to work around the error: passing 'const Library::Message' as 'this' argument discards qualifiers error without changing the signature of the handleMessage function?

2

There are 2 answers

7
W.F. On BEST ANSWER

You could also use wrapper with mutable member variable like:

#include <string>

class Message {
 public:
    std::string getData() {
        return data_;
    }
    Message(std::string data): data_{data} { }
 private:
    std::string data_;
};

class MessageWrapper {
 public:
    MessageWrapper(Message message): message{message} {}
    std::string getData() const {
        return message.getData();
    }
 private:
    mutable Message message;
};

template<class T>
void handleMessage(const T& msg) {
    std::string content = msg.getData();

}

int main() {
    MessageWrapper mw{{"abc"}};
    handleMessage(mw);
}

[live demo]

Edit:

To force const correctness you could save once retrieved data from the message e.g.:

#include <string>
#include <optional>

class Message {
 public:
    std::string getData() {
        return data_;
    }
    Message(std::string data): data_{data} { }
 private:
    std::string data_;
};

class MessageWrapper {
 public:
    MessageWrapper(Message message): message{message} {}
    std::string getData() const {
        return (data)?(*data):(*(data = message.getData()));
    }
 private:
    mutable Message message;
    mutable std::optional<std::string> data;
};

template<class T>
void handleMessage(const T& msg) {
    std::string content = msg.getData();

}

int main() {
    MessageWrapper mw{{"abc"}};
    handleMessage(mw);
}

[live demo]

3
463035818_is_not_an_ai On

You basically have two options. You could use a const_cast

template<class T>
void handleMessage(const T& msg) {
    std::string content = const_cast<T&>(msg).getData();
    // interprete and process content ...
}

this is fine as long as you know for sure that getData really does not modify any members. Or if you dont mind some overhead you can make a copy:

template<class T>
void handleMessage(const T& msg) {
    T copy = msg;
    std::string content = copy.getData();
    // interprete and process content ...
}

... or as a third option, if you want to hide the const_cast from the call site you can wrap it:

class MyMessage {
     Message msg;
public:
     std::string getData() const {
         return const_cast<Message>(msg).getData();
     }
};