--- /dev/null
+Changed: Subscriptor::subscribe cannot be called with rvalue references anymore.
+In particular, temporary string literals cannot be used as argument. Storing
+string literals with the same content at the same memory location is
+implementation defined and cannot be relied upon. On the other hand, the
+subscriber strings are not compared by their memory address anymore but by their
+actual content.
+<br>
+(Daniel Arndt, 2018/11/12)
#include <deal.II/base/exceptions.h>
#include <atomic>
+#include <cstring>
#include <map>
#include <mutex>
#include <string>
/**
* Subscribes a user of the object by storing the pointer @p validity. The
- * subscriber may be identified by text supplied as @p identifier.
+ * subscriber may be identified by text supplied as @p identifier. The latter
+ * variable must not be a temporary and its type must decay to
+ * const char *. In particular, calling this function with a rvalue reference
+ * is not allowed.
+ */
+ template <typename ConstCharStar = const char *>
+ typename std::enable_if<
+ std::is_same<ConstCharStar, const char *>::value>::type
+ subscribe(std::atomic<bool> *const validity,
+ ConstCharStar identifier = nullptr) const;
+
+ /**
+ * Calling subscribe() with a rvalue reference as identifier is not allowed.
*/
void
subscribe(std::atomic<bool> *const validity,
- const char * identifier = nullptr) const;
+ const char *&& identifier) const = delete;
/**
* Unsubscribes a user from the object.
serialize(Archive &ar, const unsigned int version);
private:
- /**
- * The data type used in #counter_map.
- */
- using map_value_type = std::map<const char *, unsigned int>::value_type;
-
- /**
- * The iterator type used in #counter_map.
- */
- using map_iterator = std::map<const char *, unsigned int>::iterator;
-
/**
* Store the number of objects which subscribed to this object. Initially,
* this number is zero, and upon destruction it shall be zero again (i.e.
*/
mutable std::atomic<unsigned int> counter;
+ /*
+ * Functor struct used for key comparison in #counter_map.
+ * Not the memory location but the actual C-string content is compared.
+ */
+ struct MapCompare
+ {
+ bool
+ operator()(const char *lhs, const char *rhs) const
+ {
+ return std::strcmp(lhs, rhs) > 0;
+ }
+ };
+
/**
* In this map, we count subscriptions for each different identification
* string supplied to subscribe().
*/
- mutable std::map<const char *, unsigned int> counter_map;
+ mutable std::map<const char *, unsigned int, MapCompare> counter_map;
+
+ /**
+ * The data type used in #counter_map.
+ */
+ using map_value_type = decltype(counter_map)::value_type;
+
+ /**
+ * The iterator type used in #counter_map.
+ */
+ using map_iterator = decltype(counter_map)::iterator;
/**
* In this vector, we store pointers to the validity bool in the SmartPointer
* Override the implementation of prepare_coarsening_and_refinement from
* the base class. This is necessary if periodic boundaries are enabled
* and the level difference over vertices over the periodic boundary
- * must be not more than 2:1.
+ * must not be more than 2:1.
*/
virtual bool
prepare_coarsening_and_refinement() override;
+template <>
void
-Subscriptor::subscribe(std::atomic<bool> *const validity, const char *id) const
+Subscriptor::subscribe<const char *>(std::atomic<bool> *const validity,
+ const char * id) const
{
std::lock_guard<std::mutex> lock(mutex);
}
+
void
Subscriptor::unsubscribe(std::atomic<bool> *const validity,
const char * id) const
{
const char *name = (id != nullptr) ? id : unknown_subscriber;
- AssertNothrow(counter > 0, ExcNoSubscriber(object_info->name(), name));
- // This is for the case that we do
- // not abort after the exception
if (counter == 0)
- return;
+ {
+ AssertNothrow(counter > 0, ExcNoSubscriber(object_info->name(), name));
+ // This is for the case that we do not abort after the exception
+ return;
+ }
std::lock_guard<std::mutex> lock(mutex);
map_iterator it = counter_map.find(name);
- if (it == counter_map.end() || it->second == 0)
+ if (it == counter_map.end())
{
AssertNothrow(it != counter_map.end(),
ExcNoSubscriber(object_info->name(), name));
- AssertNothrow(it->second > 0, ExcNoSubscriber(object_info->name(), name));
+ // This is for the case that we do not abort after the exception
+ return;
}
- else
+ if (it->second == 0)
{
- --counter;
- it->second--;
+ AssertNothrow(it->second > 0, ExcNoSubscriber(object_info->name(), name));
+ // This is for the case that we do not abort after the exception
+ return;
}
auto validity_ptr_it =
validity_ptr_it != validity_pointers.end(),
ExcMessage(
"This Subscriptor object does not know anything about the supplied pointer!"));
+ return;
}
- else
- validity_pointers.erase(validity_ptr_it);
+
+ validity_pointers.erase(validity_ptr_it);
+ --counter;
+ --it->second;
}
Subscriptor subscriptor;
std::atomic<bool> dummy_a;
- subscriptor.subscribe(&dummy_a, "a");
- subscriptor.unsubscribe(&dummy_a, "b");
- subscriptor.unsubscribe(&dummy_a, "a");
+ const char * foo_a = "a";
+ const char * foo_b = "b";
+ subscriptor.subscribe(&dummy_a, foo_a);
+ subscriptor.unsubscribe(&dummy_a, foo_b);
+ std::atomic<bool> dummy_b;
+ subscriptor.unsubscribe(&dummy_b, foo_a);
+ subscriptor.unsubscribe(&dummy_a, foo_a);
return 0;
}
--- /dev/null
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2018 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE.md at
+// the top level directory of deal.II.
+//
+// ---------------------------------------------------------------------
+
+
+
+// check that unsubscribing with a wrong id is handled correctly. This time,
+// we check that unsubscribung with a different pointer with the same content
+// works as well
+
+
+#include <deal.II/base/smartpointer.h>
+#include <deal.II/base/subscriptor.h>
+
+#include <iostream>
+#include <vector>
+
+#include "../tests.h"
+
+class Test : public Subscriptor
+{};
+
+int
+main()
+{
+ deal_II_exceptions::disable_abort_on_exception();
+
+ initlog();
+
+ Subscriptor subscriptor;
+ std::atomic<bool> dummy_a;
+ const char * foo = "a";
+ const std::string foo_string = "a";
+ subscriptor.subscribe(&dummy_a, foo);
+ subscriptor.unsubscribe(&dummy_a, foo_string.c_str());
+
+ deallog << "OK" << std::endl;
+
+ subscriptor.subscribe(&dummy_a, foo);
+ subscriptor.unsubscribe(&dummy_a, "a");
+
+ deallog << "OK" << std::endl;
+
+ return 0;
+}
--- /dev/null
+
+DEAL::OK
+DEAL::OK