Skip to content

Commit

Permalink
Building on testing/derived_in_other_header for new shared_unique_int…
Browse files Browse the repository at this point in the history
…erop_test.

Main motivation for adding this test: inform work related to
pybind/pybind11#2672

Includes a demonstration related to b/175568410 (marked with comments).

PiperOrigin-RevId: 347386087
  • Loading branch information
rwgk committed Dec 14, 2020
1 parent cf095fb commit 0bda3be
Show file tree
Hide file tree
Showing 5 changed files with 264 additions and 0 deletions.
9 changes: 9 additions & 0 deletions clif/testing/derived_in_other_header/concrete_derived.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,22 @@
#ifndef CLIF_TESTING_DERIVED_IN_OTHER_HEADER_CONCRETE_DERIVED_H_
#define CLIF_TESTING_DERIVED_IN_OTHER_HEADER_CONCRETE_DERIVED_H_

#include <iostream>

#include "clif/testing/derived_in_other_header/concrete_base.h"

namespace clif_testing {
namespace derived_in_other_header {

class ConcreteDerivedEmpty : public ConcreteBaseEmpty {
public:
// Printing from constructor & destructor for simple external validation.
ConcreteDerivedEmpty() {
std::cout << std::endl << "ConcreteDerivedEmpty+" << std::endl;
}
~ConcreteDerivedEmpty() {
std::cout << std::endl << "ConcreteDerivedEmpty-" << std::endl;
}
int Get() const { return 31607978; }
int BaseGet(const ConcreteBaseEmpty& base) { return Get() + base.Get(); }
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from "clif/testing/derived_in_other_header/python/concrete_base_clif.h" import *
from "clif/testing/derived_in_other_header/python/virtual_base_clif.h" import *

from "clif/testing/derived_in_other_header/shared_unique_interop.h":
namespace `clif_testing::derived_in_other_header`:
def make_unique_concrete_derived_empty_up_cast() -> ConcreteBaseEmpty
def make_shared_concrete_derived_empty_up_cast(
use_custom_deleter: bool = default) -> ConcreteBaseEmpty
def pass_unique_concrete_base_empty(cbe: ConcreteBaseEmpty) -> int
def pass_shared_concrete_base_empty(cbe: ConcreteBaseEmpty) -> int

def make_unique_virtual_derived_empty_up_cast() -> VirtualBaseEmpty
def make_shared_virtual_derived_empty_up_cast(
use_custom_deleter: bool = default) -> VirtualBaseEmpty
def pass_unique_virtual_base_empty(vbe: VirtualBaseEmpty) -> int
def pass_shared_virtual_base_empty(vbe: VirtualBaseEmpty) -> int
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from absl.testing import absltest
from absl.testing import parameterized

from clif.testing.derived_in_other_header.python import concrete_base
from clif.testing.derived_in_other_header.python import concrete_derived
from clif.testing.derived_in_other_header.python import shared_unique_interop as mut
from clif.testing.derived_in_other_header.python import virtual_derived

CONCRETE_BASE_EMPTY_GET_RESULT = 90146438
CONCRETE_DERIVED_EMPTY_GET_RESULT = 31607978
VIRTUAL_DERIVED_EMPTY_GET_RESULT = 29852452


class ConcreteTest(parameterized.TestCase):

def testBaseAndDerived(self):
cbe = concrete_base.ConcreteBaseEmpty()
self.assertEqual(cbe.Get(), CONCRETE_BASE_EMPTY_GET_RESULT)
cde = concrete_derived.ConcreteDerivedEmpty()
self.assertEqual(cde.Get(), CONCRETE_DERIVED_EMPTY_GET_RESULT)
self.assertEqual(
cde.BaseGet(cbe),
CONCRETE_BASE_EMPTY_GET_RESULT + CONCRETE_DERIVED_EMPTY_GET_RESULT)
self.assertEqual(
cde.BaseGet(cde),
CONCRETE_BASE_EMPTY_GET_RESULT + CONCRETE_DERIVED_EMPTY_GET_RESULT)

@parameterized.named_parameters(
('DefaultDeleter', False),
('CustomDeleter', True))
def testUnableToDisownOriginalShared(self, use_custom_deleter):
cbe = mut.make_shared_concrete_derived_empty_up_cast(use_custom_deleter)
with self.assertRaises(ValueError) as ctx:
mut.pass_unique_concrete_base_empty(cbe)
self.assertEqual(
str(ctx.exception),
'pass_unique_concrete_base_empty() argument cbe is not valid:'
' Cannot convert ConcreteBaseEmpty instance to std::unique_ptr.')

def testPassUniqueConcreteBaseEmpty(self):
cbe = mut.make_unique_concrete_derived_empty_up_cast() # b/175568410
self.assertEqual(cbe.Get(), CONCRETE_BASE_EMPTY_GET_RESULT)
i = mut.pass_unique_concrete_base_empty(cbe)
self.assertEqual(i, CONCRETE_BASE_EMPTY_GET_RESULT)
with self.assertRaises(ValueError): # Disowned.
cbe.Get()

def testOriginalUniqueNotDisownedByShared(self):
cbe = mut.make_unique_concrete_derived_empty_up_cast() # b/175568410
i = mut.pass_shared_concrete_base_empty(cbe)
self.assertEqual(i, CONCRETE_BASE_EMPTY_GET_RESULT)
self.assertEqual(cbe.Get(), CONCRETE_BASE_EMPTY_GET_RESULT)
i = mut.pass_unique_concrete_base_empty(cbe)
self.assertEqual(i, CONCRETE_BASE_EMPTY_GET_RESULT)
with self.assertRaises(ValueError): # Disowned.
cbe.Get()

@parameterized.named_parameters(
('DefaultDeleter', False),
('CustomDeleter', True))
def testPassSharedConcreteBaseEmpty(self, use_custom_deleter):
cbe = mut.make_shared_concrete_derived_empty_up_cast(use_custom_deleter)
self.assertEqual(cbe.Get(), CONCRETE_BASE_EMPTY_GET_RESULT)
i = mut.pass_shared_concrete_base_empty(cbe)
self.assertEqual(i, CONCRETE_BASE_EMPTY_GET_RESULT)
self.assertEqual(cbe.Get(), CONCRETE_BASE_EMPTY_GET_RESULT)


class VirtualTest(parameterized.TestCase):

def testBaseAndDerived(self):
vde = virtual_derived.VirtualDerivedEmpty()
self.assertEqual(vde.Get(), VIRTUAL_DERIVED_EMPTY_GET_RESULT)
self.assertEqual(vde.BaseGet(vde), 2 * VIRTUAL_DERIVED_EMPTY_GET_RESULT)

@parameterized.named_parameters(
('DefaultDeleter', False),
('CustomDeleter', True))
def testUnableToDisownOriginalShared(self, use_custom_deleter):
vbe = mut.make_shared_virtual_derived_empty_up_cast(use_custom_deleter)
with self.assertRaises(ValueError) as ctx:
mut.pass_unique_virtual_base_empty(vbe)
self.assertEqual(
str(ctx.exception),
'pass_unique_virtual_base_empty() argument vbe is not valid:'
' Cannot convert VirtualBaseEmpty instance to std::unique_ptr.')

def testPassUniqueVirtualBaseEmpty(self):
vbe = mut.make_unique_virtual_derived_empty_up_cast()
self.assertEqual(vbe.Get(), VIRTUAL_DERIVED_EMPTY_GET_RESULT)
i = mut.pass_unique_virtual_base_empty(vbe)
self.assertEqual(i, VIRTUAL_DERIVED_EMPTY_GET_RESULT)
with self.assertRaises(ValueError): # Disowned.
vbe.Get()

def testOriginalUniqueNotDisownedByShared(self):
vbe = mut.make_unique_virtual_derived_empty_up_cast()
i = mut.pass_shared_virtual_base_empty(vbe)
self.assertEqual(i, VIRTUAL_DERIVED_EMPTY_GET_RESULT)
self.assertEqual(vbe.Get(), VIRTUAL_DERIVED_EMPTY_GET_RESULT)
i = mut.pass_unique_virtual_base_empty(vbe)
self.assertEqual(i, VIRTUAL_DERIVED_EMPTY_GET_RESULT)
with self.assertRaises(ValueError): # Disowned.
vbe.Get()

@parameterized.named_parameters(
('DefaultDeleter', False),
('CustomDeleter', True))
def testPassSharedVirtualBaseEmpty(self, use_custom_deleter):
vbe = mut.make_shared_virtual_derived_empty_up_cast(use_custom_deleter)
self.assertEqual(vbe.Get(), VIRTUAL_DERIVED_EMPTY_GET_RESULT)
i = mut.pass_shared_virtual_base_empty(vbe)
self.assertEqual(i, VIRTUAL_DERIVED_EMPTY_GET_RESULT)
self.assertEqual(vbe.Get(), VIRTUAL_DERIVED_EMPTY_GET_RESULT)


if __name__ == '__main__':
absltest.main()
84 changes: 84 additions & 0 deletions clif/testing/derived_in_other_header/shared_unique_interop.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CLIF_TESTING_DERIVED_IN_OTHER_HEADER_SHARED_UNIQUE_INTEROP_H_
#define CLIF_TESTING_DERIVED_IN_OTHER_HEADER_SHARED_UNIQUE_INTEROP_H_

#include <iostream>
#include <memory>

#include "clif/testing/derived_in_other_header/concrete_base.h"
#include "clif/testing/derived_in_other_header/concrete_derived.h"
#include "clif/testing/derived_in_other_header/virtual_base.h"
#include "clif/testing/derived_in_other_header/virtual_derived.h"

namespace clif_testing {
namespace derived_in_other_header {

inline std::unique_ptr<ConcreteBaseEmpty>
make_unique_concrete_derived_empty_up_cast() {
// b/175568410: Undefined Behavior:
// ConcreteDerivedEmpty destructor does not run.
return std::unique_ptr<ConcreteDerivedEmpty>(new ConcreteDerivedEmpty);
}

inline std::shared_ptr<ConcreteBaseEmpty>
make_shared_concrete_derived_empty_up_cast(bool use_custom_deleter = false) {
if (use_custom_deleter) {
return std::shared_ptr<ConcreteDerivedEmpty>(
new ConcreteDerivedEmpty, [](ConcreteDerivedEmpty *p) { delete p; });
}
return std::shared_ptr<ConcreteDerivedEmpty>(new ConcreteDerivedEmpty);
}

inline int pass_unique_concrete_base_empty(
std::unique_ptr<ConcreteBaseEmpty> cbe) {
return cbe->Get();
}

inline int pass_shared_concrete_base_empty(
std::shared_ptr<ConcreteBaseEmpty> cbe) {
return cbe->Get();
}

inline std::unique_ptr<VirtualBaseEmpty>
make_unique_virtual_derived_empty_up_cast() {
// Well-defined behavior because VirtualDerivedEmpty has a virtual destructor.
return std::unique_ptr<VirtualDerivedEmpty>(new VirtualDerivedEmpty);
}

inline std::shared_ptr<VirtualBaseEmpty>
make_shared_virtual_derived_empty_up_cast(bool use_custom_deleter = false) {
if (use_custom_deleter) {
return std::shared_ptr<VirtualDerivedEmpty>(
new VirtualDerivedEmpty, [](VirtualDerivedEmpty *p) { delete p; });
}
return std::shared_ptr<VirtualDerivedEmpty>(new VirtualDerivedEmpty);
}

inline int pass_unique_virtual_base_empty(
std::unique_ptr<VirtualBaseEmpty> vbe) {
return vbe->Get();
}

inline int pass_shared_virtual_base_empty(
std::shared_ptr<VirtualBaseEmpty> vbe) {
return vbe->Get();
}

} // namespace derived_in_other_header
} // namespace clif_testing

#endif // CLIF_TESTING_DERIVED_IN_OTHER_HEADER_SHARED_UNIQUE_INTEROP_H_
9 changes: 9 additions & 0 deletions clif/testing/derived_in_other_header/virtual_derived.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,22 @@
#ifndef CLIF_TESTING_DERIVED_IN_OTHER_HEADER_VIRTUAL_DERIVED_H_
#define CLIF_TESTING_DERIVED_IN_OTHER_HEADER_VIRTUAL_DERIVED_H_

#include <iostream>

#include "clif/testing/derived_in_other_header/virtual_base.h"

namespace clif_testing {
namespace derived_in_other_header {

class VirtualDerivedEmpty : public VirtualBaseEmpty {
public:
// Printing from constructor & destructor for simple external validation.
VirtualDerivedEmpty() {
std::cout << std::endl << "VirtualDerivedEmpty+" << std::endl;
}
~VirtualDerivedEmpty() {
std::cout << std::endl << "VirtualDerivedEmpty-" << std::endl;
}
int Get() const override { return 29852452; }
int BaseGet(const VirtualBaseEmpty& base) { return Get() + base.Get(); }
};
Expand Down

0 comments on commit 0bda3be

Please sign in to comment.