>From a2692f0abaa83f1c1482cdeab2dbc0f389f6fd8b Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 20 Sep 2015 00:04:29 +0200 Subject: [PATCH 2/2] Use new deserialize_cast() to respect strict aliasing rules This commit fixes the same problem as the previous one but in the actuarial table code, where readability is more important and hence, instead of just directly using std::memcpy(), it uses a new deserialize_cast() helper function which wraps memcpy() in an easier to use interface. --- Makefile.am | 1 + actuarial_table.cpp | 11 ++++++----- deserialize_cast.hpp | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 deserialize_cast.hpp diff --git a/Makefile.am b/Makefile.am index 0a42dde..b7050d8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1083,6 +1083,7 @@ noinst_HEADERS = \ dbvalue.hpp \ death_benefits.hpp \ default_view.hpp \ + deserialize_cast.hpp \ docmanager_ex.hpp \ docmdichildframe_ex.hpp \ edit_mvc_docview_parameters.hpp \ diff --git a/actuarial_table.cpp b/actuarial_table.cpp index 680d053..8a4c179 100644 --- a/actuarial_table.cpp +++ b/actuarial_table.cpp @@ -30,6 +30,7 @@ #include "alert.hpp" #include "assert_lmi.hpp" +#include "deserialize_cast.hpp" #include "materially_equal.hpp" #include "miscellany.hpp" #include "oecumenic_enumerations.hpp" // methuselah @@ -84,7 +85,7 @@ t = invalid; char z[sizeof(T)]; is.read(z, sizeof(T)); - t = *reinterpret_cast(z); + t = deserialize_cast(z); LMI_ASSERT(invalid != t); return t; } @@ -574,13 +575,13 @@ void soa_actuarial_table::find_table() while(index_ifs) { int index_table_number = - *reinterpret_cast(index_record) + deserialize_cast(index_record) ; if(table_number_ == index_table_number) { char* p = 54 + index_record; - boost::int32_t z = *reinterpret_cast(p); - table_offset_ = std::streampos(static_cast(z)); + int z = deserialize_cast(p); + table_offset_ = std::streampos(z); break; } index_ifs.read(index_record, index_record_length); @@ -810,7 +811,7 @@ void soa_actuarial_table::read_values(std::istream& is, int nominal_length) for(int j = 0; j < number_of_values; ++j) { is.read(z, sizeof(double)); - data_[j] = *reinterpret_cast(z); + data_[j] = deserialize_cast(z); } } diff --git a/deserialize_cast.hpp b/deserialize_cast.hpp new file mode 100644 index 0000000..4065f4b --- /dev/null +++ b/deserialize_cast.hpp @@ -0,0 +1,52 @@ +// Completely safe yet convenient to use cast from "char*". +// +// Copyright (C) 2015 Gregory W. Chicares. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +// +// http://savannah.nongnu.org/projects/lmi +// email: +// snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA + +// $Id$ + +#ifndef deserialize_cast_hpp +#define deserialize_cast_hpp + +#include "config.hpp" + +#include // std::memcpy() + +/// Reinterpret the contents of the buffer as a value of the given (POD) type T. +/// +/// The effect of this function is very close to "*reinterpret_cast(buf)", +/// but it avoids breaking the "strict aliasing" rules of the language and so +/// can't result in undefined behaviour. Subsidiarily, it also doesn't elicit a +/// warning from g++ with -O2 or higher, unlike such a type-punning-breaking +/// reinterpret_cast. + +template +inline T deserialize_cast(char const* buf) +{ + // Notice that the use of memcpy() is completely optimized away by all the + // major compilers (g++, clang, msvc), the generated code is as efficient + // as if "return *reinterpret_cast(buf)" were used here, but, + // unlike the latter, this code doesn't break the "strict aliasing" rules. + T z; + std::memcpy(&z, buf, sizeof(z)); + return z; +} + +#endif // deserialize_cast_hpp + -- 2.5.1