Skip to main content
gamedev.net
🔒 Locked

C++ Exercise

Started by Daggerbot 9 replies 2.2k views
Original Post
Daggerbot
Daggerbot
I was at school working on a non-school related project, writing string class that can easily append any type. I got carried away playing with C++'s sexy template features, and ended up with a program that can write out the name of /any/ type.
// main.cpp

#include <iostream>
#include <typehelper.h>

using namespace std;

int main( int argc, char* argv[] )
{
  TypeHelper< ... >::Class::PrintTypeName();
  cout << endl;
  return 0;
}



So here's the exercise. Write typehelper.h so that you will get the correct output, no matter what you plug into TypeHelper<...>. Well, that may take a bit too long, so if you can explain what you would have to do to achieve this, you get rated up...and a cookie. Here are some examples of types and the correct output.
TypeHelper< int*** >
pointer to pointer to pointer to signed integer (32)

TypeHelper< const bool**const*& >
reference to pointer to constant pointer to pointer to constant boolean

TypeHelper< long double >
floating point (96)

TypeHelper< unsigned long long& >
reference to unsigned integer (64)
Again, if you can achieve this, I'll give you all the money in my wallet. EDIT: Bonus pencil if it works with functions (with a limited number of parameters of course.
TypeHelper< void(*)(int,int) >
function pointer returning void ( signed integer (32), signed integer (32) )
[Edited by - Daggerbot on March 18, 2009 7:55:23 PM]
swiftcoder
swiftcoder
The obvious solution - manual template specialisation, ala std::type_traits:
template <typename T>struct TypeHelper{	static std::string name() {return std::string();}};template <>struct TypeHelper<int ***>{	static std::string name() {return "pointer to pointer to pointer to integer (32)";}};template <>struct TypeHelper<const bool**const*&>{	static std::string name() {return "reference to pointer to constant pointer to pointer to constant boolean";}};template <>struct TypeHelper<long double>{	static std::string name() {return "floating point (96)";}};// ...
Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]
ToohrVyk
ToohrVyk
Er... those are easy. A C++ type is basically a tree, where nodes are "pointer to T", "constant T", "reference to T", "N-sized array of T", and so on. Template specialization allows you to perform basic pattern matching on types, which means you can easily write a meta-algorithm that constructs something for pointers and something else for non-pointers.

Here's a quick example for handling pointers and integers. You can use the same approach to add other nodes (references, arrays of size N, function pointers...) and other leaves (int, unsigned int, long, unsigned char...).

template <typename T>struct name{};template <typename T>struct name<T*>{  static std::string get()  {    return std::string("pointer to ") + name<T>::get();  }};template <>struct name<int>{  static std::string get() { return "int"; }};int main(){  std::cout << name<int**>::get() << "\n";}


Where this gets difficult is if you want to support any function pointer (these get nasty, because you can have any number of arguments) and any class (because there's no clean way to get the actual human-readable name of a type).
Daggerbot
Daggerbot
That works, but it should work even after changing the type plugged into TypeHelper<>, so that you don't have to specialize for ridiculous types like int****************************.

EDIT: Not for the previous reply..
Daggerbot
Daggerbot
This doesn't quite work for every type. Enums and unions wouldn't work (to my knowledge) and you would have to add a special function to classes and structs.

class MyClass{public  static void PrintTypeName()  {    cout << "MyClass";  }};
MaulingMonkey
MaulingMonkey
This is pretty basic template recursion & partial specialization. What's more fun is to make it break!

#include <boost/preprocessor.hpp>#include <iostream>#include <typehelper.h>#define STARS                    BOOST_PP_ENUM_PARAMS( 100, * BOOST_PP_INTERCEPT )#define SHITTONS_OF_THEM         BOOST_PP_ENUM_PARAMS( 100, STARS BOOST_PP_INTERCEPT )#define METRIC_SHITTONS_OF_THEM  BOOST_PP_ENUM_PARAMS( 100, SHITTONS_OF_THEM BOOST_PP_INTERCEPT )int main() {    TypeHelper< void METRIC_SHITTONS_OF_THEM >::Class::PrintName();    std::cout << std::endl;    return 0;}


Of course, I suddenly find myself curious if template recursion limits are the only errors I run into with this code.
swiftcoder
swiftcoder
Quote:
Original post by Daggerbot
This doesn't quite work for every type. Enums and unions wouldn't work (to my knowledge) and you would have to add a special function to classes and structs.
You can get some mileage out of typeid(T).name() (make sure RTTI is enabled for your compiler), but the results vary across compilers.
Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]
MaulingMonkey
MaulingMonkey
Maybe slightly overkill.

#include <boost/preprocessor.hpp>#include <iostream>#define STARS                    BOOST_PP_ENUM_PARAMS( 100, * BOOST_PP_INTERCEPT )#define SHITTONS_OF_THEM         BOOST_PP_ENUM_PARAMS( 100, STARS BOOST_PP_INTERCEPT )#define METRIC_SHITTONS_OF_THEM  BOOST_PP_ENUM_PARAMS( 100, SHITTONS_OF_THEM BOOST_PP_INTERCEPT )template < typename T > struct TypeHelper { struct Class { static void PrintName() { std::cout << typeid(T).name(); } }; };template < typename T > struct TypeHelper< T* > { struct Class { static void PrintName() { std::cout << "A pointer to "; TypeHelper<T>::Class::PrintName(); } }; };int main() {	TypeHelper< void METRIC_SHITTONS_OF_THEM >::Class::PrintName();	// 1>.\main.cpp(12) : fatal error C1060: compiler is out of heap space	std::cout << std::endl;	return 0;}
MaulingMonkey
MaulingMonkey
But reducing the numbers to 10 and I realize I've a syntax error somewhere in my mess.

EDIT: Oh yes, how very silly of me, of course BOOST_PP_ENUM_* puts in commas...
MaulingMonkey
MaulingMonkey
#include <boost/preprocessor.hpp>#include <iostream>#define ECHO(z,n,data) data#define STARS                    BOOST_PP_REPEAT( 100, ECHO, * )#define SHITTONS_OF_THEM         BOOST_PP_REPEAT( 100, ECHO, STARS )#define METRIC_SHITTONS_OF_THEM  BOOST_PP_REPEAT( 100, ECHO, SHITTONS_OF_THEM )template < typename T > struct TypeHelper { struct Class { static void PrintName() { std::cout << typeid(T).name(); } }; };template < typename T > struct TypeHelper< T* > { struct Class { static void PrintName() { std::cout << "A pointer to "; TypeHelper<T>::Class::PrintName(); } }; };int main() {	TypeHelper< void METRIC_SHITTONS_OF_THEM >::Class::PrintName();	// 1>.\main.cpp(13) : fatal error C1026: parser stack overflow, program too complex	std::cout << std::endl;	return 0;}


Sadly it seems this error occurs for anything > 10x10x10 (1000) ish, and the program runs as fine as programmed for anything at or bellow that -- sure, the capitalization is bad and it isn't properly internationalized for translation -- but none of that is a template recursion overflow of any sort like I was hoping for so it seems I've failed.
the_edd
the_edd
Quote:
Original post by Daggerbot
So here's the exercise. Write typehelper.h so that you will get the correct output, no matter what you plug into TypeHelper<...>. Well, that may take a bit too long, so if you can explain what you would have to do to achieve this, you get rated up...and a cookie.


This is one of the earlier exercises in Template Meta-programming by Abrahams and Gurtovoy. You might be interested the book if you like this kind of thing.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.