0

Suppose I have a class that wraps a dictionary (std::map). The class has getter and setter to manipulate values. To simplify the code I would like to have some helper function that will provide access to a specified node by name. The problem that such a function should be called from both const and non-const context. The code is as follows:

class MyTest
{
public:
    void set(const std::string_view &name, const std::string_view &value)
    {
        auto *it = get_private(name);
        if(it != nullptr)
        {
            *it = value;
        }
        else
        {
            m_arr.insert({name, value});
        }
    }

    std::string_view get(const std::string_view &name) const
    {
        auto *it = get_private(name);
        if(it != nullptr)
        {
            return *it;
        }

        return "";
    }

private:
    const std::string_view *get_private(const std::string_view &name) const
    {
        auto it = std::find_if(m_arr.begin(), m_arr.end(), [name](const auto &pair) -> bool {
            return pair.first == name;
        });

        if(it == m_arr.end())
        {
            return nullptr;
        }

        return &(it->second);
    }
    
    std::string_view *get_private(const std::string_view &name)
    {
        auto it = std::find_if(m_arr.begin(), m_arr.end(), [name](const auto &pair) -> bool {
            return pair.first == name;
        });

        if(it == m_arr.end())
        {
            return nullptr;
        }

        return &(it->second);
    }

private:
    std::map<std::string_view, std::string_view> m_arr;
};

and usage as follows:

MyTest mt;
mt.set("name1", "value1");
mt.set("name2", "value3");

std::string_view v = mt.get("name1");
std::cout << v << std::endl;

But I don't like the idea to have 2 versions of the helper function - const and non-const.

I'm thinking of something like this:

template <typename T>
typename std::conditional<std::is_same<T, std::string_view>::value, std::string_view, const std::string_view>::type
*get_private(const std::string_view &name)
{
}

but I have no clue how to make the function itself const/non-const. Anyway I get the error:

Candidate template ignored: couldn't inter template argument 'T'

How can I declare such a function?

P.S. According to the Misra/Autosar rules I can't use const_cast casting.

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
folibis
  • 12,048
  • 6
  • 54
  • 97
  • This is one of the pain points in C++. About the only thing that can be done is have the two functions a wrapper around a private template function that carries the bulk of the logic. – Sam Varshavchik May 02 '23 at 12:20
  • this can be solve by deduce this. but I think `const_cast` is the cleanest way. – apple apple May 02 '23 at 12:29
  • Have one be a thunk to the other, so the logic is in one spot: `std::string_view* get_private(std::string_view const& name) { auto const* cthis = this; return const_cast(cthis->get_private(name)); }` – Eljay May 02 '23 at 12:31
  • fwiw, you can rewrite the `get_private` to accept `this` and deduce from it. – apple apple May 02 '23 at 12:36
  • yes, `const_cast` could solve the problem if I could use it. Unfortunately I can't. – folibis May 02 '23 at 12:39
  • why can you not use `const_cast` ? – 463035818_is_not_an_ai May 02 '23 at 12:43
  • 1
    oh, due to Misra, thats bad. It is completely ok to warn you about the usage of `const_cast`, but not being able to use it at all is bad. – 463035818_is_not_an_ai May 02 '23 at 12:46
  • imho you need to fix your tool chain rather than working around it, when it forbids you to use proper c++ idioms – 463035818_is_not_an_ai May 02 '23 at 12:49
  • 1
    AUTOSAR rules - `A cast shall not remove any const or volatile qualification from the type` – folibis May 02 '23 at 12:50
  • @463035818_is_not_a_number Misra isn't a toolchain but some rules set by a consortium, and they don't look like a compromising bunch... – Passer By May 02 '23 at 13:05
  • @PasserBy I know, my guess is that somewhere in their toolchain OP has something that prevents them to use it in their code. I suppose the consortium itself doesnt care much about one individual breaking one of their rules ;) – 463035818_is_not_an_ai May 02 '23 at 13:07
  • do they allow c-style casts? – 463035818_is_not_an_ai May 02 '23 at 13:09
  • 1
    @folibis Off-topic, but consider passing `std::string_view` [by value](https://stackoverflow.com/questions/27256377/c-view-types-pass-by-const-or-by-value). – PaulMcKenzie May 02 '23 at 13:10
  • This is not a duplicate. The question is tagged MISRA. Posting an answer suggesting to use casts (C++ or C style doesn't matter) is not an answer to the question. Furthermore, MISRA C++ does not allow any features beyond C++03 other than what various MISRA C++ addendums/TC explicitly say is fine to use. Including the dangerous `auto` "feature" from C++11 which does not belong in a (safety-related) program. – Lundin May 03 '23 at 07:51
  • As one of the answers in the dupe postulates, if you won't cast, your common impl can be a **static** member function template that accepts the instance as another parameter. There is no need to reopen this just for posting "valuable" opinions from the peanut gallery. – StoryTeller - Unslander Monica May 03 '23 at 08:06

1 Answers1

0

The idea goes back to an item in one of Scott Meyers book. I don't have it at hand, hence cannot refer to it directly. Though the pattern goes like this:

struct foo {
    int value = 42;

    const int& get() const { return value; }

    int& get() { return const_cast<int&>(const_cast<const foo*>(this)->get()); }
};

In int& get() the this pointer is non-const. It is always safe to cast it to a const foo*. This is done to call the const overload. Then, because we know that inside int& get(), the this pointer is not actually const we can cast the reference returned from const int& get() const to a non-const reference. This is a situation where const_cast is safe, because we know that the actual object is not const.

TL;DR: only implement the const overload, and use it to implement the non-const one.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185