Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Possibly useful technique for templatizing const and non-const method variants

30 views
Skip to first unread message

Alf P. Steinbach

unread,
Jan 7, 2018, 6:17:11 AM1/7/18
to
This is possibly a technique that others (e.g. you?) might find useful,
for avoiding redundancy for the case where a bunch of methods should
each have non-const and const version, but with the same code.

One severe limitation is that as I can't see any good way to support
virtual methods this way?

Code (the cppx::* stuff is omitted here, it's pretty trivial):


----------------------------------------------------------------------
#pragma once
#include <cppx/constness.hpp> // cppx::const_like_
#include <cppx/class-kind/No_copy.hpp>
#include <npp/Encoding_config.hpp> // npp::Encoding_config
#include <npp/paths.hpp> // npp::path_to_gui_config
#include <stdlib/extension/hopefully_and_fail.hpp>
#include <stdlib/filesystem.hpp> // std::filesystem::*
#include <stdlib/memory.hpp> // std::unique_ptr
#include <stdlib/string.hpp> // std::string
#include <wrapped-tinyxml2/all.hpp> // tinyxml2::*

namespace npp {
using namespace std::literals;
using namespace stdlib::ext::hopefully_and_fail;
using namespace stdlib::ext::type_builders;

namespace fs = std::filesystem;
namespace tx2 = tinyxml2;

using cppx::const_like_;
using cppx::Constness;
using cppx::No_copy;
using cppx::with_constness_;
using std::string;
using std::unique_ptr;
using stdlib::ext::char_path_or_x;

using Path = fs::path;

class Config;

namespace config_impl {

template< class Type >
auto non_null( ref_<const string> msg, const ptr_<Type> p )
-> ptr_<Type>
{
hopefully( p != nullptr )
or fail( "npp::Config::" + msg );
return p;
}

template< Constness::Enum constness > // m or c, for
mutable or const
class Const_adaptation_
{
protected:
using Self = with_constness_<constness, Config>;

static auto root( ref_<Self> self )
-> ptr_<const_like_<Self, tx2::XMLElement>>
{
return non_null( "root - no xml root element",
self.p_xml_->RootElement() );
}

static auto gui_configs( ref_<Self> self )
-> ptr_<const_like_<Self, tx2::XMLElement>>
{
return non_null(
"gui_configs - no xml GUIConfigs element",
root( self )->FirstChildElement( "GUIConfigs" )
);
}

static auto config_of_new_doc( ref_<Self> self )
-> ptr_<const_like_<Self, tx2::XMLElement>>
{
auto p_config = gui_configs( self )->FirstChildElement();
while( p_config != nullptr )
{
const ptr_<const char> p_name =
p_config->Attribute( "name" );
if( p_name != nullptr and p_name ==
"NewDocDefaultSettings"s )
{
break;
}
p_config = p_config->NextSiblingElement();
}
return non_null(
"config_of_new_doc - no xml element named
NewDocDefaultSettings",
p_config
);
}
};
} // namespace config_impl

class Config
: public No_copy
, private config_impl::Const_adaptation_<Constness::m>
, private config_impl::Const_adaptation_<Constness::c>
{
unique_ptr<tx2::XMLDocument> p_xml_; // Dynamic in order
to be non-const.

using Consted = config_impl::Const_adaptation_<Constness::c>;
friend Consted;
using Mutable = config_impl::Const_adaptation_<Constness::m>;
friend Mutable;

using Consted::config_of_new_doc;
using Mutable::config_of_new_doc;

public:
auto default_encoding() const
-> Encoding_config
{
return Encoding_config{ *config_of_new_doc( *this ) };
}

void set_default_encoding( ref_<const Encoding_config> values )
{
values.set_as_attributes_in( *config_of_new_doc( *this ) );
}

auto to_string() const
-> string
{
tx2::XMLPrinter printer;
p_xml_->Print( &printer );
return printer.CStr();
}

Config( ref_<const Paths> paths = {} )
: p_xml_{ new tx2::XMLDocument{} }
{
const Path config_path = paths.gui_config_file();
hopefully( fs::exists( config_path ) )
or fail( "npp::Config::<init> - specified config file
doesn't exist" );
const string ansi_config_path = char_path_or_x( config_path );
p_xml_->LoadFile( ansi_config_path.c_str() ) >> Success{}
or fail( "npp::Config::<init> -
tinyxml2::XMLDocument::LoadFile failed" );
//hopefully( xml_.RootElement()->Name() == "NotepadPlus"s )
// or fail( "npp::Config::<init> - XML root element is
not “NotepadPlus”" );
}
};

} // namespace npp
----------------------------------------------------------------------


Cheers!

- Alf

Mr Flibble

unread,
Jan 7, 2018, 12:09:05 PM1/7/18
to
Sorry Alf but your code is an over-engineered, overly complicated
unreadable mess.

This is all you need to do to simply and elegantly solve the problem:

const foo& bar::baz() const
{
...
}

foo& bar::baz()
{
return const_cast<foo&>(const_cast<const bar*>(this)->baz());
}

/Flibble

--
"Suppose it’s all true, and you walk up to the pearly gates, and are
confronted by God," Bryne asked on his show The Meaning of Life. "What
will Stephen Fry say to him, her, or it?"
"I’d say, bone cancer in children? What’s that about?" Fry replied.
"How dare you? How dare you create a world to which there is such misery
that is not our fault. It’s not right, it’s utterly, utterly evil."
"Why should I respect a capricious, mean-minded, stupid God who creates
a world that is so full of injustice and pain. That’s what I would say."

Alf P. Steinbach

unread,
Jan 7, 2018, 3:01:31 PM1/7/18
to
On 1/7/2018 6:08 PM, Mr Flibble wrote:
> On 07/01/2018 11:16, Alf P. Steinbach wrote:
>> This is possibly a technique that others (e.g. you?) might find
>> useful, for avoiding redundancy for the case where a bunch of methods
>> should each have non-const and const version, but with the same code.
>>
<snip>
>
>
> Sorry Alf but your code is an over-engineered, overly complicated
> unreadable mess.
>
> This is all you need to do to simply and elegantly solve the problem:
>
> const foo& bar::baz() const
> {
>     ...
> }
>
> foo& bar::baz()
> {
>     return const_cast<foo&>(const_cast<const bar*>(this)->baz());
> }
>

Perhaps you thought that there exists cases where it can be a good idea
to do what you do here, hence it must always be the best idea on Earth,
with no need for anything else.

Essentially, with the casts you're giving up static type checking, which
of course can be fine -- and often is fine, that's why we have casts.
But it's not a general solution: in some cases your code's
const_cast<foo&> can lead to Undefined Behavior, namely when the foo is
original const (and that includes when it's dynamically allocated as
const) and the resulting reference is used. So you appear, at best, to
not have understood the purpose of a cast-free approach.

I'm sorry, but that means your evaluation is not worth shit.


Cheers!,

- Alf

Mr Flibble

unread,
Jan 7, 2018, 3:42:28 PM1/7/18
to
On 07/01/2018 19:45, Alf P. Steinbach wrote:
> On 1/7/2018 6:08 PM, Mr Flibble wrote:
>> On 07/01/2018 11:16, Alf P. Steinbach wrote:
>>> This is possibly a technique that others (e.g. you?) might find
>>> useful, for avoiding redundancy for the case where a bunch of methods
>>> should each have non-const and const version, but with the same code.
>>>
> <snip>
>>
>>
>> Sorry Alf but your code is an over-engineered, overly complicated
>> unreadable mess.
>>
>> This is all you need to do to simply and elegantly solve the problem:
>>
>> const foo& bar::baz() const
>> {
>>      ...
>> }
>>
>> foo& bar::baz()
>> {
>>      return const_cast<foo&>(const_cast<const bar*>(this)->baz());
>> }
>>
>
> Perhaps you thought that there exists cases where it can be a good idea
> to do what you do here, hence it must always be the best idea on Earth,
> with no need for anything else.
>
> Essentially, with the casts you're giving up static type checking, which

The casts comply with static type checking (unlike reinterpret_cast or
C-style cast which I am not using) so this first claim of yours is bullshit.

> of course can be fine -- and often is fine, that's why we have casts.
> But it's not a general solution: in some cases your code's
> const_cast<foo&> can lead to Undefined Behavior, namely when the foo is
> original const (and that includes when it's dynamically allocated as
> const) and the resulting reference is used. So you appear, at best, to
> not have understood the purpose of a cast-free approach.

Obviously the intention here is to return a reference to something that
is non-const (as we are calling non-const baz()). You will only get
undefined behaviour if you invoke undefined behaviour. You only have a
bug if you write a bug so your second claim is diversionary tactic at
best and bullshit at worst.

>
> I'm sorry, but that means your evaluation is not worth shit.

Try again Alf; your solution is egregious and my solution is simple and
all that is required.

Mr Flibble

unread,
Jan 7, 2018, 4:00:52 PM1/7/18
to
Of course for the few cases where my solution would invoke UB you would
simply write a friend function template that the const and non-const
member functions call rather than use the over-engineered mess that you
are proposing.
0 new messages