252 lines
6.9 KiB
C++
252 lines
6.9 KiB
C++
/**
|
|
* @file cppfeatures_test
|
|
* @author Vir
|
|
* @date 2021-03
|
|
* @brief cpp features
|
|
*
|
|
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
|
|
* Second Life Viewer Source Code
|
|
* Copyright (C) 2021, Linden Research, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License only.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
// Tests related to newer C++ features, for verifying support across compilers and platforms
|
|
|
|
#include "linden_common.h"
|
|
#include "../test/lltut.h"
|
|
|
|
namespace tut
|
|
{
|
|
|
|
struct cpp_features_test {};
|
|
typedef test_group<cpp_features_test> cpp_features_test_t;
|
|
typedef cpp_features_test_t::object cpp_features_test_object_t;
|
|
tut::cpp_features_test_t tut_cpp_features_test("LLCPPFeatures");
|
|
|
|
// bracket initializers
|
|
// Can initialize containers or values using curly brackets
|
|
template<> template<>
|
|
void cpp_features_test_object_t::test<1>()
|
|
{
|
|
S32 explicit_val{3};
|
|
ensure(explicit_val==3);
|
|
|
|
S32 default_val{};
|
|
ensure(default_val==0);
|
|
|
|
std::vector<S32> fibs{1,1,2,3,5};
|
|
ensure(fibs[4]==5);
|
|
}
|
|
|
|
// auto
|
|
//
|
|
// https://en.cppreference.com/w/cpp/language/auto
|
|
//
|
|
// Can use auto in place of a more complex type specification, if the compiler can infer the type
|
|
template<> template<>
|
|
void cpp_features_test_object_t::test<2>()
|
|
{
|
|
std::vector<S32> numbers{3,6,9};
|
|
|
|
// auto element
|
|
auto& aval = numbers[1];
|
|
ensure("auto element", aval==6);
|
|
|
|
// auto iterator (non-const)
|
|
auto it = numbers.rbegin();
|
|
*it += 1;
|
|
S32 val = *it;
|
|
ensure("auto iterator", val==10);
|
|
}
|
|
|
|
// range for
|
|
//
|
|
// https://en.cppreference.com/w/cpp/language/range-for
|
|
//
|
|
// Can iterate over containers without explicit iterator
|
|
template<> template<>
|
|
void cpp_features_test_object_t::test<3>()
|
|
{
|
|
|
|
// Traditional iterator for with container
|
|
//
|
|
// Problems:
|
|
// * Have to create a new variable for the iterator, which is unrelated to the problem you're trying to solve.
|
|
// * Redundant and somewhat fragile. Have to make sure begin() and end() are both from the right container.
|
|
std::vector<S32> numbers{3,6,9};
|
|
for (auto it = numbers.begin(); it != numbers.end(); ++it)
|
|
{
|
|
auto& n = *it;
|
|
n *= 2;
|
|
}
|
|
ensure("iterator for vector", numbers[2]==18);
|
|
|
|
// Range for with container
|
|
//
|
|
// Under the hood, this is doing the same thing as the traditional
|
|
// for loop above. Still uses begin() and end() but you don't have
|
|
// to access them directly.
|
|
std::vector<S32> numbersb{3,6,9};
|
|
for (auto& n: numbersb)
|
|
{
|
|
n *= 2;
|
|
}
|
|
ensure("range for vector", numbersb[2]==18);
|
|
|
|
// Range for over a C-style array.
|
|
//
|
|
// This is handy because the language determines the range automatically.
|
|
// Getting this right manually is a little trickier.
|
|
S32 pows[] = {1,2,4,8,16};
|
|
S32 sum{};
|
|
for (const auto& v: pows)
|
|
{
|
|
sum += v;
|
|
}
|
|
ensure("for C-array", sum==31);
|
|
}
|
|
|
|
// override specifier
|
|
//
|
|
// https://en.cppreference.com/w/cpp/language/override
|
|
//
|
|
// Specify that a particular class function is an override of a virtual function.
|
|
// Benefits:
|
|
// * Makes code somewhat easier to read by showing intent.
|
|
// * Prevents mistakes where you think something is an override but it doesn't actually match the declaration in the parent class.
|
|
// Drawbacks:
|
|
// * Some compilers require that any class using override must use it consistently for all functions.
|
|
// This makes switching a class to use override a lot more work.
|
|
|
|
class Foo
|
|
{
|
|
public:
|
|
virtual bool is_happy() const = 0;
|
|
};
|
|
|
|
class Bar: public Foo
|
|
{
|
|
public:
|
|
bool is_happy() const override { return true; }
|
|
// Override would fail: non-const declaration doesn't match parent
|
|
// bool is_happy() override { return true; }
|
|
// Override would fail: wrong name
|
|
// bool is_happx() override { return true; }
|
|
};
|
|
|
|
template<> template<>
|
|
void cpp_features_test_object_t::test<4>()
|
|
{
|
|
Bar b;
|
|
ensure("override", b.is_happy());
|
|
}
|
|
|
|
// final
|
|
//
|
|
// https://en.cppreference.com/w/cpp/language/final: "Specifies that a
|
|
// virtual function cannot be overridden in a derived class or that a
|
|
// class cannot be inherited from."
|
|
|
|
class Vehicle
|
|
{
|
|
public:
|
|
virtual bool has_wheels() const = 0;
|
|
};
|
|
|
|
class WheeledVehicle: public Vehicle
|
|
{
|
|
public:
|
|
virtual bool has_wheels() const final override { return true; }
|
|
};
|
|
|
|
class Bicycle: public WheeledVehicle
|
|
{
|
|
public:
|
|
// Error: can't override final version in WheeledVehicle
|
|
// virtual bool has_wheels() override const { return true; }
|
|
};
|
|
|
|
template<> template<>
|
|
void cpp_features_test_object_t::test<5>()
|
|
{
|
|
Bicycle bi;
|
|
ensure("final", bi.has_wheels());
|
|
}
|
|
|
|
// deleted function declaration
|
|
//
|
|
// https://en.cppreference.com/w/cpp/language/function#Deleted_functions
|
|
//
|
|
// Typical case: copy constructor doesn't make sense for a particular class, so you want to make
|
|
// sure the no one tries to copy-construct an instance of the class, and that the
|
|
// compiler won't generate a copy constructor for you automatically.
|
|
// Traditional fix is to declare a
|
|
// copy constructor but never implement it, giving you a link-time error if anyone tries to use it.
|
|
// Now you can explicitly declare a function to be deleted, which has at least two advantages over
|
|
// the old way:
|
|
// * Makes the intention clear
|
|
// * Creates an error sooner, at compile time
|
|
|
|
class DoNotCopy
|
|
{
|
|
public:
|
|
DoNotCopy() {}
|
|
DoNotCopy(const DoNotCopy& ref) = delete;
|
|
};
|
|
|
|
template<> template<>
|
|
void cpp_features_test_object_t::test<6>()
|
|
{
|
|
DoNotCopy nc; // OK, default constructor
|
|
//DoNotCopy nc2(nc); // No, can't copy
|
|
//DoNotCopy nc3 = nc; // No, this also calls copy constructor (even though it looks like an assignment)
|
|
}
|
|
|
|
// defaulted function declaration
|
|
//
|
|
// https://en.cppreference.com/w/cpp/language/function#Function_definition
|
|
//
|
|
// What about the complementary case to the deleted function declaration, where you want a copy constructor
|
|
// and are happy with the default implementation the compiler will make (memberwise copy).
|
|
// Now you can explicitly declare that too.
|
|
// Usage: I guess it makes the intent clearer, but otherwise not obviously useful.
|
|
class DefaultCopyOK
|
|
{
|
|
public:
|
|
DefaultCopyOK(): mVal(123) {}
|
|
DefaultCopyOK(const DefaultCopyOK&) = default;
|
|
S32 val() const { return mVal; }
|
|
private:
|
|
S32 mVal;
|
|
};
|
|
|
|
template<> template<>
|
|
void cpp_features_test_object_t::test<7>()
|
|
{
|
|
DefaultCopyOK d; // OK
|
|
DefaultCopyOK d2(d); // OK
|
|
DefaultCopyOK d3 = d; // OK
|
|
ensure("default copy d", d.val()==123);
|
|
ensure("default copy d2", d.val()==d2.val());
|
|
ensure("default copy d3", d.val()==d3.val());
|
|
}
|
|
|
|
|
|
} // namespace tut
|