Assume we are working with Clang strictly. No other compiler is being used. Also note that Clang supports CXX ABI.
We are using C++14.
Normally, we would get demangled class name like so:
#include <cxxabi.h>
class GoodClass {
public:
virtual const char *foo() const noexcept;
}
const char *
GoodClass::foo() const noexcept
{
// Naive implementation, not gonna' check any errors and stuff.
int32_t status = 0;
return abi::__cxa_demangle(typeid(*this).name(), 0, 0, &status);
}
This method will help us when we need class names of public subclasses of this class:
class SomeSubclassOfGoodClass : public GoodClass { }
SomeSubclassOfGoodClass object;
std::cout << object.foo(); // prints "SomeSubclassOfGoodClass"
However, in static methods, we could not use this since there is no instance. Therefore, it is impossible to serve an object to the typeid directive.
The examplary method serves well (with polymorphism), however it needs an instance to operate. This would involve problems about OO (such as constructors).
What would you do in a situation like this?
Thank you for your attention.
The use of demangle needs a little work. At the moment you have a memory leak.
Here's one way to solve that:
#include <cxxabi.h>
#include <memory>
#include <iostream>
#include <string>
#include <typeinfo>
#include <typeindex>
#include <cassert>
#include <stdexcept>
struct demangled_string
{
using ptr_type = std::unique_ptr<char, void(*)(void*)>;
demangled_string(ptr_type&& ptr) noexcept;
const char* c_str() const;
operator std::string() const;
std::ostream& write(std::ostream& os) const;
private:
ptr_type _ptr;
};
inline std::ostream& operator<<(std::ostream& os, const demangled_string& str)
{
return str.write(os);
}
inline std::string operator+ (std::string l, const demangled_string& r) {
return l + r.c_str();
}
inline std::string operator+(const demangled_string& l, const std::string& r)
{
return std::string(l) + r;
}
demangled_string demangle(const char* name);
demangled_string demangle(const std::type_info& type);
demangled_string demangle(std::type_index type);
template<class T>
demangled_string demangle(T* p) {
return demangle(typeid(*p));
}
template<class T>
demangled_string demangle()
{
return demangle(typeid(T));
}
// implementation
demangled_string::demangled_string(ptr_type&& ptr) noexcept
: _ptr(std::move(ptr))
{}
std::ostream& demangled_string::write(std::ostream& os) const
{
if (_ptr) {
return os << _ptr.get();
}
else {
return os << "{nullptr}";
}
}
const char* demangled_string::c_str() const
{
if (!_ptr)
{
throw std::logic_error("demangled_string - zombie object");
}
else {
return _ptr.get();
}
}
demangled_string::operator std::string() const {
return std::string(c_str());
}
demangled_string demangle(const char* name)
{
using namespace std::string_literals;
int status = -4;
demangled_string::ptr_type ptr {
abi::__cxa_demangle(name, nullptr, nullptr, &status),
std::free
};
if (status == 0) return { std::move(ptr) };
switch(status)
{
case -1: throw std::bad_alloc();
case -2: {
std::string msg = "invalid mangled name~";
msg += name;
auto p = (char*)std::malloc(msg.length() + 1);
strcpy(p, msg.c_str());
return demangled_string::ptr_type { p, std::free };
}
case -3:
assert(!"invalid argument sent to __cxa_demangle");
throw std::logic_error("invalid argument sent to __cxa_demangle");
default:
assert(!"PANIC! unexpected return value");
throw std::logic_error("PANIC! unexpected return value");
}
}
demangled_string demangle(const std::type_info& type)
{
return demangle(type.name());
}
demangled_string demangle(std::type_index type)
{
return demangle(type.name());
}
std::string method(const demangled_string& cls, const char* method)
{
return std::string(cls) + "::" + method;
}
// test
class test_class
{
using this_class = test_class;
static auto classname() { return demangle<this_class>(); }
public:
static void test1() {
std::cout << method(demangle<this_class>(), __func__) << std::endl;
std::cout << method(classname(), __func__) << std::endl;
}
void test2() {
std::cout << method(demangle(this), __func__) << std::endl;
std::cout << method(classname(), __func__) << std::endl;
}
};
int main()
{
test_class t;
t.test1();
t.test2();
}
expected output:
test_class::test1
test_class::test1
test_class::test2
test_class::test2
The typeid operator may also be applied to a type, not just an expression: typeid(GoodClass) ought to work when you cannot access this.
Edit: without an instance you need to turn to static polymorphism. You could have a mix in base class Identifiable<X> which has a static method with the code you suggested above, but using typeid(X) instead. Your classes need to extend this class passing themselves as the template parameter (the curiously recursive template pattern), but it is not possible to ensure that a class does so:
class C : public Identifiable<C> {}; // method returns C
class D : public Identifiable<C> {}; // also returns C
Related
I've been having some trouble recently with an invoker class I've created. Reason is so I can parse an address through the class constructor and have it handle calling methods that are in memory. Problem is, when I go to return a class object type, it throws an error "Cannot convert from std::string to InvokerType". Any help is appreciated.
template <class InvokerType>
class Invoker {
public:
Invoker(std::intptr_t address) :
address(address)
{ }
template<class... Arguments>
InvokerType invoke(Arguments&&... parameters) {
InvokerType func = (InvokerType)this->address;
return (InvokerType)func(std::forward<Arguments>(parameters)...);
}
private:
std::intptr_t address;
};
std::string test(const std::string& str) {
std::cout << str << std::endl;
return "I have returned";
}
int main(void) {
using test_print_type = std::string (*)(const std::string& str);
Invoker<test_print_type> get_value{ (std::intptr_t)test };
auto ret = get_value.invoke("Hello I am a testing function\n");
std::cout << ret << std::endl; // Errors here
std::system("PAUSE");
return 0;
}
If you want to return the value from the invoked function, I think you need to separate the return type into its own template parameter.
#include <cstdint>
#include <string>
#include <iostream>
#include <utility>
template <class ReturnType, class...Args>
class Invoker {
public:
Invoker(std::intptr_t address) :
address(address)
{ }
ReturnType invoke(Args&&... parameters) {
auto func = (ReturnType(*)(Args...)) address;
return func(std::forward<Args>(parameters)...);
}
private:
std::intptr_t address;
};
std::string test(const std::string& str) {
std::cout << str << std::endl;
return "I have returned";
}
int main(void) {
Invoker<std::string, const std::string&> get_value{ (std::intptr_t)test };
auto ret = get_value.invoke("Hello I am a testing function\n");
std::cout << ret << std::endl; // Errors here
return 0;
}
So I have implemented a rather trivial logger but I would like to extend it so I can pass arguments of data to it, possibly with formatting and I can't seem to figure out how to best do it.
So far it is written like so:
// Standard Headers.
#include <ostream>
#include <variant>
#include <memory>
#include <utility>
#include <mutex>
#include <array>
#include <string_view>
#include <iostream>
namespace Logger {
// Various logging severity levels.
enum class Level {
Info
};
class Log {
public:
// Takes standard streams cout, cerr, etc.
explicit Log(std::ostream& p_stream) : m_log(&p_stream) {}
// Create logger using std::make_unique<std::ofstream>(...) so ownership is passed.
explicit Log(std::unique_ptr<std::ostream> p_stream) : m_log(std::move(p_stream)) {}
template <typename T>
inline void info(T&& p_message);
private:
template <typename T>
void log(T&& p_msg) const {
auto const t_lock = std::lock_guard(*m_lock);
std::visit([&](auto&& p_ptr) {
(*p_ptr) << p_msg;
}, m_log);
};
std::ostream& stream() const {
return std::visit([](auto&& ptr) -> std::ostream& {
return *ptr;
}, m_log);
}
template <typename T>
inline void add(Logger::Level p_level, T&& p_message);
std::variant<std::unique_ptr<std::ostream>, std::ostream*> m_log;
std::unique_ptr<std::mutex> m_lock = std::make_unique<std::mutex>();
std::array<std::string_view, 1> m_levels = { "Info" };
};
template <typename T>
void Log::add(Level p_level, T&& p_message) {
auto const f_lock = std::lock_guard(*m_lock);
stream() << m_levels[static_cast<size_t>(p_level)] << ": " << p_message << '\n';
}
template <typename T>
inline void Log::info(T&& p_message) {
add(Level::Info, p_message);
}
}
int main() {
auto logger = Logger::Log(std::cout);
logger.info("Hello, world!");
return 0;
}
What I would like to do is when I use .info() be able to specify any number of arguments which will be replaced when writing to the log, similar to this:
logger.info("Some error message with arg: {}", 1);
How would I go about doing this? What is the best approach?
I usually use std::any. This is a non optimized version (uses copies instead of references, removes item from vector etc) but the basic idea is to convert a compile-time parameter pack to a run time one with this line:
std::vector<std::any> a = {args ...};
You can use references with, say, std::vector<std::any> a = {std::ref(args)...};
The full function would be like that:
template<typename ... many>
void safe_printf2(const char *s, many ... args)
{
using namespace std;
vector<any> a = {args ...};
while (*s) {
if (*s == '%') {
if (*(s + 1) == '%') {
++s;
}
else {
if (a.empty())
throw logic_error("Fewer arguments provided to printf");
if (a[0].type() == typeid(string)) cout << any_cast<string>(a[0]);
if (a[0].type() == typeid(int)) cout << any_cast<int>(a[0]);
if (a[0].type() == typeid(double)) cout << any_cast<double>(a[0]);
a.erase(a.begin());
s++;
}
}
cout << *s++;
}
}
Example:
safe_printf2("Hello % how are you today? I have % eggs and your height is %","Jack"s, 32,5.7);
For some weird reasons, I do something similar this way (you will have to adapt it to your needs):
inline
std::ostream &
txt(std::ostream &output,
const char *format)
{
return output << format;
}
template<typename First,
typename ...Args>
inline
std::ostream &
txt(std::ostream &output,
const char *format,
First &&first,
Args &&...args)
{
while(*format)
{
if(*format=='%')
{
return txt(output << std::forward<First>(first),
++format, std::forward<Args>(args)...);
}
output << *format++;
}
return output;
}
The two previous functions use the marker % in a format string to inject the next argument (whatever it is, a value, a format specifier, a custom object...) in the stream.
Of course some adapters could ease the usage:
template<typename ...Args>
inline
std::string
txt(const char *format,
Args &&...args)
{
std::ostringstream output;
txt(output, format, std::forward<Args>(args)...);
return output.str();
}
For example:
std::ostream &
operator<<(std::ostream &output,
const MyStuff &ms)
{
return output << '[' << ms.member1 << '|' << ms.member2 << ']';
}
...
MyStuff my_stuff= ... ;
auto msg=txt("an integer %, a (formatted) real %% and something else %\n",
12, std::setprecision(12), 34.56, my_stuff);
Then it should be possible to adjust Log::add() (then Log::info() and anything related) to make it usable in a way similar to what was expected in the question
logger.info("Some error message with arg: %", 1);
Hope that helps.
Why is Assert::AreSame() failing?
C++ class to be tested:
template <typename T>
class XPtr
{
private:
T* p;
public:
XPtr(T* ptr) {
p = ptr;
}
XPtr(const XPtr& ptr) {
p = ptr.p;
}
T* operator&() {
return p;
}
// The following declaration compiles but still
// is it the correct way of declaring friend function?
template<typename T>
friend std::wstring getString(const XPtr<T>& p);
};
template<typename T>
std::wstring getString(const XPtr<T>& p) {
std::wstringstream ss;
ss << L"{" << p.p;
if (p.p != 0) { ss << L"," << *p.p; }
ss << L"}";
return ss.str();
}
Native C++ Unit Test is as follows:
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace Microsoft {
namespace VisualStudio {
namespace CppUnitTestFramework {
//Actually I would like to specialize for XPtr<T> instead of XPtr<int>,
//how to do it?
template<>
static std::wstring ToString<XPtr<int> >(const class XPtr<int> & t)
{ return getString<int>(t); }
}
}
}
namespace TestRCPtr1
{
TEST_CLASS(UnitTest1)
{
public:
TEST_METHOD(TestMethod6)
{
XPtr<int> p1(new int(1));
XPtr<int> p2(p1);
Assert::AreEqual(&p1, &p2); // Test passes
Assert::AreSame(p1, p2); // Test fails, see below for error message
}
};
}
Result Message: Assert failed. Expected:<{000000001BE465D0,1}> Actual:<{000000001BE465D0,1}>
Question 1: As per my understanding Assert::AreSame() should compare with logic &p1==&p2 and pass successfully. What is going wrong here?
Question 2: How to specialize Microsoft::VisualStudio::CppUnitTestFramework::ToString() (see at top of unit test code) for XPtr<T> instead of XPtr<int> ?
In C++ you can use virtual methods to have following code work as you expect:
#include <iostream>
#include <string>
class BaseClass {
public:
virtual std::string class_name() const { return "Base Class"; }
};
class FirstClass : public BaseClass {
int value = 1;
public:
std::string class_name() const { return "FirstClass"; }
};
class SecondClass : public BaseClass {
long long value = -1;
public:
std::string class_name() const { return "SecondClass"; }
};
int main() {
const int array_size = 5;
const bool in_first_mode = true;
void *data;
int sample_size;
if (in_first_mode) {
data = new FirstClass[array_size];
sample_size = sizeof(FirstClass);
} else {
data = new SecondClass[array_size];
sample_size = sizeof(SecondClass);
}
// this is class-independent code
for (int index = 0; index < array_size; ++index) {
BaseClass *pointer = static_cast<BaseClass*>(data + index * sample_size);
std::cout << pointer->class_name() << std::endl;
}
return 0;
}
This will work correctly for both in_first_mode = true and in_first_mode = false.
So, basically, when you want to write code that works for both classes you can just use pointer to the BaseClass.
But what if you already given data buffer, filled with data of type TypeOne, TypeTwo, TypeThree or TypeFour, and in runtime you know that type, which stored in int type. Problem is that TypeOne, TypeTwo, TypeThree and TypeFour have not inherited from one base class. In my case, actually, they are structs from 3rd party library, which is already compiled C-compatible library, so I can not modify it. I want to get something like pointer from the example above, but problem arises with identifying what C++ type should have this pointer.
It there a more elegant type-casting alternative to making C++ class wrappers to these four types (which gives something similar to the example above), and to making pointer be void * and necessity of
if (type == 1) {
TypeOne *type_one_pointer = static_cast<TypeOne*>(pointer);
// do something
} else if (type == 2) {
/* ... */
}
every time I use pointer?
If the classes are unrelated, you can store them in a std::variant (or use Boost.Variant if your compiler is not C++17 compliant) and access the value with a visitor. This is more flexible than templates, as it allows you to include types with a different interface in the variant type.
For example (I did not compile this code):
#include <iostream>
#include <string>
#include <variant>
#include <vector>
struct TypeOne {
std::string class_name() const { return "Type one"; }
};
struct TypeTwo {
int value = 1;
std::string class_name() const { return "Type two"; }
};
struct TypeThree {
long long value = -1;
// note the different function signature
static std::string class_name() { return "Type three"; }
};
struct TypeFour {
std::string getMyClassName() const { return "Type four"; }
};
struct Visitor {
template <class T>
void operator ()(T&& value) const {
std::cout << value.class_name() << std::endl;
}
// special case
void operator ()(const TypeFour& value) const {
std::cout << value.getMyClassName() << std::endl;
}
};
int main() {
typedef std::variant<TypeOne, TypeTwo, TypeThree, TypeFour> Variant;
std::vector<Variant> values;
values.emplace_back(TypeOne{});
values.emplace_back(TypeTwo{});
values.emplace_back(TypeThree{});
values.emplace_back(TypeFour{});
for (const auto& var : values) {
std::visit(Visitor{}, var);
}
}
Thanks to #ForEveR, I find the solution. I need to use templates.
It means that if in the example above FirstClass and SecondClass would have no BaseClass one can do so:
#include <iostream>
#include <string>
class FirstClass {
int value = 1;
public:
std::string class_name() const { return "FirstClass"; }
};
class SecondClass {
long long value = -1;
public:
std::string class_name() const { return "SecondClass"; }
};
template <typename T>
void do_my_stuff(void* void_pointer) {
T *pointer = static_cast<T*>(void_pointer);
std::cout << pointer->class_name() << std::endl;
}
int main() {
const int array_size = 5;
const bool in_first_mode = true;
void *data;
int sample_size;
if (in_first_mode) {
data = new FirstClass[array_size];
sample_size = sizeof(FirstClass);
} else {
data = new SecondClass[array_size];
sample_size = sizeof(SecondClass);
}
for (int index = 0; index < array_size; ++index) {
if (in_first_mode) {
do_my_stuff<FirstClass>(data + index * sample_size);
} else {
do_my_stuff<SecondClass>(data + index * sample_size);
}
}
return 0;
}
According to cppreference calling std::unique_ptr::operator*() is equivalent to calling *(std::unique_ptr::get()).
However I'm getting different results for both calls. Here is my code:
#include <iostream>
#include <string>
#include <memory>
#include <fcntl.h>
#include <unistd.h>
struct file_descriptor
{
private:
struct closer;
public:
typedef int handle_type;
typedef closer closer_type;
constexpr static handle_type kInvalidHandle = -1;
public:
file_descriptor(int handle = kInvalidHandle) : handle_{ handle } { }
file_descriptor(std::nullptr_t) : file_descriptor{ } { }
operator int&() { return handle_; }
operator int() const { return handle_; }
int& operator*() { return static_cast<int&>(*this); }
int operator*() const { return static_cast<int>(*this); }
bool operator==(const file_descriptor& other) const
{ return (handle_ == other.handle_); }
bool operator!=(const file_descriptor& other) const
{ return !(*this == other); }
private:
struct closer
{
typedef file_descriptor pointer;
void operator()(pointer handle) const
{ ::close(*handle); }
};
int handle_;
};
using unique_file_ptr = std::unique_ptr<typename file_descriptor::handle_type,
typename file_descriptor::closer_type>;
unique_file_ptr managed_open(const std::string& path)
{
return { ::open(path.c_str(), O_RDWR), { } };
}
int main(int, char**)
{
auto handle = managed_open("/dev/random");
std::cout << "*handle : " << *handle << std::endl;
std::cout << "*handle.get(): " << *handle.get() << std::endl;
}
My output (live output here):
*handle : 4198400
*handle.get(): 3
Please note that *handle.get() returns the correct value, while *handle doesn't.
Why am I getting different results?
Here's what happens. unique_ptr<T, D>::get() returns D::pointer - in your case, a temporary file_descriptor constructed from the int handle. Then its operator* calls its operator int& which returns a reference to handle_ stored inside that temporary.
When you call *handle.get() directly, the int& reference it produces is used before the temporary dies.
But when you call *handle instead, you call handle.operator*() which in turn calls handle.get(). The implementation of operator*, with everything expanded, becomes something like this:
int& operator*() {
file_descriptor temp(internal_pointer_);
int& result = temp.handle_;
return result;
}
The function returns a reference to the member of a temporary. That temporary dies at the closing brace, and the reference becomes dangling, whereupon the program exhibits undefined behavior triggered by accessing an object after its lifetime has ended.
I suggest you re-think the solution. Here's an arguably cleaner way to approach it:
#include <iostream>
#include <string>
#include <memory>
#include <stdexcept>
#include <utility>
#include <fcntl.h>
#include <unistd.h>
struct managed_file {
managed_file() noexcept {};
managed_file(const std::string& path)
: _handle { ::open(path.c_str(), O_RDWR) }
{
if (_handle == -1) {
throw std::runtime_error { std::string { "failed to open file " } + path };
}
}
managed_file(const managed_file&) = delete;
managed_file(managed_file&& other) noexcept
: _handle { other._handle }
{
other._handle = -1;
}
managed_file& operator=(const managed_file&) = delete;
managed_file& operator=(managed_file&& other) noexcept {
managed_file tmp { std::move(other) };
using std::swap;
swap(_handle, other._handle);
return *this;
}
virtual ~managed_file() noexcept
{
close();
}
void close() noexcept {
if (_handle != -1) {
::close(_handle);
_handle = -1;
}
}
const int& operator*() const noexcept {
return _handle;
}
private:
int _handle = -1;
};
managed_file managed_open(const std::string& path)
{
return managed_file { path };
}
using namespace std;
int main(int, char**)
{
cout << "opening" << endl;
auto handle = managed_open("/dev/random");
cout << "checking" << endl;
std::cout << "*handle : " << *handle << std::endl;
}