Specializing formatting for pointer to class - fmt

I need to provide format-magic to a class hierarchy and usually those things are passed around via pointer. This example here works currently on master:
#include <type_traits>
#include <sstream>
#include <fmt/format.h>
#include <fmt/ostream.h>
struct A {
virtual ~A() {}
virtual std::string name() const { return "A"; }
};
struct B : A {
virtual std::string name() const { return "B"; }
};
template <typename T>
struct fmt::formatter<T, std::enable_if_t<std::is_base_of<A, std::remove_const_t<std::remove_pointer_t<T>>>::value, char>> : fmt::formatter<std::string> {
template <typename FormatCtx>
auto format(const A* a, FormatCtx& ctx) {
return fmt::formatter<std::string>::format(a->name(), ctx);
}
};
template<class... T>
[[noreturn]] void err(fmt::format_string<T...> s, T&&... args) {
throw std::logic_error(fmt::format(s, std::forward<T>(args)...));
}
int main() {
A* a = new A();
B* b = new B();
const A* x = new A();
const B* y = new B();
fmt::print("{}, {}, {}, {}\n", a, b, x, y);
std::ostringstream oss;
fmt::print(oss, "{}\n", a);
err("{}, {}, {}, {}\n", a, b, x, y);
}
However, when I'm going to the latest release 8.1.1, I'm getting:
error: static assertion failed: Formatting of non-void pointers is disallowed.
Now my question is: What's the plan here? Can I do this in future versions of fmt or is this more of an accident?

This is definitely a regression. Supporting a hierarchy of classes can be done as documented in https://fmt.dev/latest/api.html#formatting-user-defined-types using references instead of pointers:
#include <type_traits>
#include <fmt/format.h>
struct A {
virtual ~A() {}
virtual std::string name() const { return "A"; }
};
struct B : A {
virtual std::string name() const { return "B"; }
};
template <typename T>
struct fmt::formatter<T, std::enable_if_t<std::is_base_of<A, T>::value, char>> :
fmt::formatter<std::string> {
template <typename FormatCtx>
auto format(const A& a, FormatCtx& ctx) {
return fmt::formatter<std::string>::format(a.name(), ctx);
}
};
int main() {
B b;
A& a = b;
fmt::print("{}", a); // prints "B"
}

Related

How to enumerate all member variables of a class / struct in c++

I'm working on some kind of simple reflection for c++ structs where i want to recursivly iterate over all member variables.
The code below almost does what i want but my compiler complians: "recursive type or function dependency context too complex" coming form aggregate_arity<MemberType>::size() which is based on Orients aggregate_arity implementation.
Example usage case:
struct B
{
SPVStruct;
var_t<float2_t, true> f4;
};
struct A
{
SPVStruct;
var_t<float2_t, true> f2;
var_t<float3_t, true> f3;
float d;
B b;
};
A a{};
InitializeStruct<A, true>(a);
Implementation:
struct TSPVStructTag {};
#ifndef SPVStruct
#define SPVStruct typedef TSPVStructTag SPVStructTag;
#endif
template< class, class = std::void_t<> >
struct has_spv_tag : std::false_type { };
template< class T >
struct has_spv_tag<T, std::void_t<typename T::SPVStructTag>> : std::true_type { };
template <class T>
void InitVar(T& _Member) {}
template <class T, bool Assemble>
void InitVar(var_t<T, Assemble>& _Member)
{
// actual stuff happening here
}
template <size_t N, class T, bool Assemble>
void InitStruct(T& _Struct)
{
if constexpr(N > 0u)
{
auto& member = get<N-1>(_Struct);
using MemberType = typename std::decay_t<decltype(member)>;
if constexpr(has_spv_tag<MemberType>::value)
{
constexpr size_t n = aggregate_arity<MemberType>::size(); // this is the complex recursion that blows up
InitStruct<n, MemberType, Assemble>(member);
}
else
{
InitVar(member);
InitStruct<N - 1, T, Assemble>(_Struct);
}
}
}
template <class T, bool Assemble>
void InitializeStruct(T& _Struct)
{
constexpr size_t N = aggregate_arity<T>::size();
InitStruct<N, T, Assemble>(_Struct);
}
Example
I use the has_spv_tag to mark structs that should be reflected. I can't wait for c++20 with actual reflection support :(
Thanks for your help!
Edit:
I got it to compile and changed the iteration order. Now a different problem comes up: constexpr size_t M = aggregate_arity::size() returns 0 even for the same type it returned the correct value earlier. i verified that the type is infact the same (first struct type B) by comparing the hash from typeid. How is it possible to that aggregate returns two different values for the exact same type?
template <class T, bool Assemble>
constexpr bool is_var_t(var_t<T, Assemble>& _Member) { return true; }
template <class T>
constexpr bool is_var_t(T& _Member) { return false; }
template <class T>
void InitVar(T& _Member) { std::cout << typeid(T).name() << std::endl; }
template <class T, bool Assemble>
void InitVar(var_t<T, Assemble>& _Member)
{
// actual stuff happening here
std::cout << typeid(T).name() << std::endl;
}
template <size_t n, size_t N, class T>
void InitStruct(T& _Struct)
{
std::cout << "n " << n << " N " << N << std::endl;
if constexpr(n < N)
{
decltype(auto) member = get<n>(_Struct);
using MemberType = std::remove_cv_t<decltype(member)>;
std::cout << typeid(MemberType).hash_code() << std::endl;
if (is_var_t(member))
{
InitVar(member);
InitStruct<n + 1, N, T>(_Struct);
}
else
{
constexpr size_t M = aggregate_arity<MemberType>::size();
InitStruct<0, M, MemberType>(member);
}
}
}
Edit 2: example for the new version: http://coliru.stacked-crooked.com/a/b25a84454d53d8de
Antony Polukhin pointed out the problem: MemberType still had the reference from get(_Struct). The code works with
MemberType = std::remove_reference_t<std::remove_cv_t<decltype(member)>>;
template <size_t n, size_t N, class T>
void InitStruct(T& _Struct)
{
if constexpr(n < N)
{
decltype(auto) member = get<n>(_Struct);
using MemberType = std::remove_reference_t<std::remove_cv_t<decltype(member)>>;
if constexpr(has_spv_tag<MemberType>::value)
{
InitStruct<0, aggregate_arity<MemberType>::size(), MemberType>(member);
}
else
{
InitVar(member);
}
InitStruct<n + 1, N, T>(_Struct);
}
}
I now use has_spv_tag<MemberType>::value to identify which member is a struct that i want to enumerate. There was also a bug with the order of InitStruct<n + 1, N, T>(_Struct);

How do I write to use Dictionary<T,U>.Enumerator with #:native extern

It seems
#:native('System.Collections.Generic.Dictionary')
extern class Dictionary<T, U>
is valid, but it does not seems to apply to nested class of generic class.
I want to write in Haxe, with my own 'extern class' bridge, something like:
struct K {
string x;
}
struct V {
string y;
}
class Foo {
public void foo(Dictionary<K, V> d) {
Dictionary<K,V>.Enumerator e = d.GetEnumerator();
while (e.MoveNext()) {
Debug.Log(e.Current.Key.x);
Debug.Log(e.Current.Value.y);
}
}
}

templated class that contains another templated class

I am writing templated classes, specifically a templated class that stores another templated class in an array. I am having trouble with getting the pair members thing1 and thing2 set. They are getting printed out as garbage variables.
#ifndef ARRAY_H
#define ARRAY_H
#include"Pair.h"
#include<iostream>
using namespace std;
template<typename T>
class Array
{
template<typename U>
friend ostream & operator<<(ostream & out, const Array<U> &a);
private:
int size;
T* tPtr;
public:
Array(int s);
Array();
~Array();
T& operator[](int sub);
T operator[](int sub)const;
const Array<T> &operator =(const Array<T> &);
int getLength(){return size;};
};
template<typename T>
Array<T>::Array():size(0),tPtr(NULL)
{
}
template<typename T>
Array<T>::Array(int s):size(s)
{
tPtr = new T [size];
}
template<typename T>
Array<T>::~Array()
{
delete [] tPtr;
}
template<typename T>
T& Array<T>::operator[](int sub)
{
if(sub<0 || sub>size)
throw out_of_range("subscript out of range");
return tPtr[sub];
}
template<typename T>
T Array<T>::operator[](int sub)const
{
if(sub<0 || sub>size)
throw out_of_range("subscript out of range");
return tPtr[sub];
}
template<typename T>
const Array<T> &Array<T>::operator =(const Array<T> &right)
{
if(&right !=this)
{
if(size!=right.size)
{
delete [] tPtr;
size=right.size;
tPtr= new T[size];
}
for(int i =0;i<size;i++)
tPtr[i] = right.tPtr[i];
}
return *this;
}
template<typename U>
ostream & operator<<(ostream &out, const Array<U> &a)
{
for(int i =0;i<a.size;i++)
{
out<<a.tPtr[i];
}
out<<endl;
return out;
}
#endif
#ifndef PAIR_H
#define PAIR_H
#include <iostream>
using namespace std;
template<typename T,typename U>
class Pair {
private:
T thing1;
U thing2;
public:
Pair(){};
Pair( T thing1, U thing2) : thing1(thing1), thing2(thing2) {}
ostream & display(ostream & out=cout) const;
};
template<typename T,typename U>
ostream & Pair<T, U>::display(ostream & out) const {
return (out << "(" << thing1 << ", " << thing2 << ")");
}
template<typename T,typename U>
ostream & operator<<(ostream & out, const Pair<T,U> & pair) {
return pair.display(out);
}
#endif
#include "Pair.h"
#include "Array.h"
#include<string>
#include<iostream>
using namespace std;
template<typename T, typename U>
Array< Pair<T, U> >zip(Array<T> & lhs,Array<U> & rhs)
{
int zipLen = (lhs.getLength() < rhs.getLength() ? lhs.getLength() : rhs.getLength());
Array< Pair<T, U> >zipped(zipLen);
for (int i=0; i<zipLen; i++)
zipped[i] = Pair<T, U>(lhs[i], rhs[i]);
return zipped;//return array object
}
int main()
{
Array<int> a1(5);
Array<char>a2(3);
Array<Pair<int,char>>a3;
for(int i =1;i<5;i++)
a1[i-1]=i;
for(char ch='a';ch<='c';ch++)
a2[ch-'a']=ch;
a3=zip(a1,a2);//this is the line where I seem to lose all my data
cout<<a3;
system("pause");
return 0;
}
zipped is a local object. When the scope is over, its destructor will be called. Also the destructor of the two Array class objects inside zipped object will also get called. In the Array class the destructor, you are deleting the dynamic allocation. That's why you are see the garbage values. :)

Visual C++ won't let me use template?

Here's a quick template Dequeue object I made and it worked fine on mac terminal but visual c++ won't work for me. Sorry ahead of time if this is a dumb question, I'm a bit new to C++ and templates. Here's the code:
#include <iostream>
using namespace std;
template <typename T>
class Dequeue;
template <typename T>
class node;
template <typename T>
ostream& operator<<(ostream&, const Dequeue<T>&);
template <typename T>
class node{
public:
node<T>* next;
node<T>* prev;
T data;
node(T data=T(), node<T>* next=NULL, node<T>* prev=NULL);
};
template <typename T>
node<T>::node(T _data, node<T>* _next, node<T>* _prev){
data = _data;
next = _next;
prev = _prev;
}
template <typename T>
class Dequeue{
private:
node<T>* head;
node<T>* tail;
int _size;
void clear();
public:
Dequeue();
~Dequeue();
Dequeue(const Dequeue<T>&);
const Dequeue<T>& operator=(const Dequeue<T>&);
friend ostream& operator<< <>(ostream&, Dequeue<T>&);
void push_front(T&);
void push_back(T&);
void pop_front();
void pop_back();
T front();
T back();
int size();
bool isEmpty();
};
template <typename T>
Dequeue<T>::Dequeue(){
_size = 0;
head = NULL;
tail = NULL;
}
template <typename T>
Dequeue<T>::~Dequeue(){
clear();
}
template <typename T>
void Dequeue<T>::clear(){
while(_size > 0)
pop_front();
}
template <typename T>
Dequeue<T>::Dequeue(const Dequeue<T>& other){
head = NULL;
tail = NULL;
_size = 0;
*this = other;
}
template <typename T>
const Dequeue<T>& Dequeue<T>::operator=(const Dequeue<T>& other){
if(this = &other)
return *this;
clear();
node<T>* np = other.head;
while(np!=NULL){
node<T>* p = new node<T>(np->data);
if(head == tail == NULL)
head = tail = p;
else{
tail->next = p;
p->prev = tail;
}
tail = p;
np = np->next;
}
_size = other._size;
return *this;
}
template <typename T>
ostream& operator<<(ostream& out_str, Dequeue<T>& other){
node<T>* np = other.head;
while(np != NULL){
out_str << np->data << endl;
np = np->next;
}
return out_str;
}
template <typename T>
void Dequeue<T>::push_front(T& object){
node<T>* np = new node<T>(object);
np->next = head;
head->prev = np;
head = np;
_size++;
}
template <typename T>
void Dequeue<T>::push_back(T& object){
node<T>* np = new node<T>(object);
tail->next = np;
np->prev = tail;
tail = np;
_size++;
}
template <typename T>
void Dequeue<T>::pop_front(){
node<T>* np = head;
head = head->next;
delete np;
head->prev=NULL;
_size--;
}
template <typename T>
void Dequeue<T>::pop_back(){
node<T>* np = tail;
tail = np->prev;
delete np;
tail->next = NULL;
size--;
}
template <typename T>
T Dequeue<T>::front(){
return head->data;
}
template <typename T>
T Dequeue<T>::back(){
return tail->data;
}
template <typename T>
int Dequeue<T>::size(){
return _size;
}
template <typename T>
bool Dequeue<T>::isEmpty(){
if(size == 0)
return true;
else
return false;
}
int main(){
Dequeue<int> A;
A.push_back(5);
A.push_front(6);
A.push_back(7);
cout << A << endl;
cin.get();
return 0;
}
And here are the errors I'm getting:
1>------ Build started: Project: Kevin, Configuration: Debug Win32 ------
1> Dequeue.cpp
1>c:\users\kevin nguyen\documents\visual studio 2010\projects\kevin\kevin\dequeue.cpp(184): error C2664: 'Dequeue<T>::push_back' : cannot convert parameter 1 from 'int' to 'int &'
1> with
1> [
1> T=int
1> ]
1>c:\users\kevin nguyen\documents\visual studio 2010\projects\kevin\kevin\dequeue.cpp(185): error C2664: 'Dequeue<T>::push_front' : cannot convert parameter 1 from 'int' to 'int &'
1> with
1> [
1> T=int
1> ]
1>c:\users\kevin nguyen\documents\visual studio 2010\projects\kevin\kevin\dequeue.cpp(186): error C2664: 'Dequeue<T>::push_back' : cannot convert parameter 1 from 'int' to 'int &'
1> with
1> [
1> T=int
1> ]
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Your issue is that in your declarations, you're trying to take a reference to T. In this case, the literals you are passing in to your function ( A.push_back(5), for example) is not necessarily something that can be taken a reference of. Your declaration should probably look more like this:
/* use `const` */
void Dequeue<T>::push_front( const T& object )
/* Same for push_back: use `const` */
void Dequeue<T>::push_back( const T& object )
Const will allow you to pass constant data as a reference. You make a copy of it anyways, so make sure all your declarations all the way down use const if they are taking a reference.
Also, it may be wise not to tell your T data parameter in your node to default to a freshly created T. That will force any data type you use with your Linked List to have a default constructor. Just take a const T& data instead, or no T at all.

How to compile this VC++ program?

Im very new to VC++. Yesterday my VC++ instructor gave us this code and asked us to make it as a exe. I have no idea of where to start and end. How to make this single file into exe. How and where to paste this working code in visual studio. If my question sounds too dumb, sorry. But i'm. Please help me out in making an exe from this single file. By the way this is Josephus circle algorithm
Code :
//////////////////Header file
#include<iostream.h>
template <class T>
class ex
{
private:
struct node
{
T data;
struct node *next;
};
struct node *head,*front,*rear;
public:
ex()
{
head=new node;
head->next=NULL;
front=rear=head;
}
void enqueue(T x);
T dequeue();
void print();
void move_next();
};
//////////////////Implementation file
#include "ex.h"
template <class T>
void ex<T>::enqueue(T x)
{
node *p;
p=new node;
p->data=x;
if(head->next==NULL)
{
front=rear=p;
head->next=p;
p->next=p;
}
else
{
rear->next=p;
p->next=front;
rear=rear->next;
}
}
template<class T>
T ex<T>::dequeue()
{
node *t;
T x;
t=front;
x=t->data;
front=front->next;
rear->next=front;
delete(t);
return x;
}
template<class T>
void ex<T>::print()
{
node *p=front;
do
{
cout<<p->data<<endl;
p=p->next;
}while(p!=rear->next);
}
template<class T>
void ex<T>::move_next()
{
front=front->next;
rear=rear->next;
}
/////////////////Application file
#include "ex.cpp"
void main()
{
ex<int> e;
int m,n,i,d;
cout<<"Enter the number of people";
cin>>n;
cout<<"Enter the number of passes";
cin>>m;
for(i=1;i<=n;i++)
e.enqueue(i);
cout<<"The players are
";
e.print();
cout<<"Eliminated in order
";
while(n>1)
{
for(i=1;i<=m;i++)
e.move_next();
d=e.dequeue();
cout<<d<<endl;
n--;
}
d=e.dequeue();
cout<<"Winning player: "<<d<<endl;
}
Make the first file named ex.h
#include<iostream.h>
template <class T>
class ex
{
private:
struct node
{
T data;
struct node *next;
};
struct node *head,*front,*rear;
public:
ex()
{
head=new node;
head->next=NULL;
front=rear=head;
}
void enqueue(T x);
T dequeue();
void print();
void move_next();
};
Second file into ex.cpp
#include "ex.h"
template <class T>
void ex<T>::enqueue(T x)
{
node *p;
p=new node;
p->data=x;
if(head->next==NULL)
{
front=rear=p;
head->next=p;
p->next=p;
}
else
{
rear->next=p;
p->next=front;
rear=rear->next;
}
}
template<class T>
T ex<T>::dequeue()
{
node *t;
T x;
t=front;
x=t->data;
front=front->next;
rear->next=front;
delete(t);
return x;
}
template<class T>
void ex<T>::print()
{
node *p=front;
do
{
cout<<p->data<<endl;
p=p->next;
}while(p!=rear->next);
}
template<class T>
void ex<T>::move_next()
{
front=front->next;
rear=rear->next;
}
And the third file into Main.cpp or something.
#include "ex.cpp"
void main()
{
ex<int> e;
int m,n,i,d;
cout<<"Enter the number of people";
cin>>n;
cout<<"Enter the number of passes";
cin>>m;
for(i=1;i<=n;i++)
e.enqueue(i);
cout<<"The players are
";
e.print();
cout<<"Eliminated in order
";
while(n>1)
{
for(i=1;i<=m;i++)
e.move_next();
d=e.dequeue();
cout<<d<<endl;
n--;
}
d=e.dequeue();
cout<<"Winning player: "<<d<<endl;
}
Then compile it. Also, it's supposed to be int main() not void main()
To add to Alex F. answer, you will also need to create an ex.h file and an ex.cpp file and paste their respective code inside

Resources