6

Currently I have:

template <typename T> struct typename_struct<T*> { static char const* name() { return (std::string(typename_struct<T>::name()) + "*").c_str(); } }; 

I wonder if I can avoid the whole bit where I'm forced to allocate a string to perform the concatenation.

This is all happening at compile time, i.e. I intend to get the string "int****" when I reference typename_struct<int****>::name(). (Do assume that I have declared a corresponding specialization for int which returns "int")

As the code is written now, does the compiler do the concatenation with std::string during compile time only? (I would be okay with that) Or does such a call result in 4 std::string based concatenations at runtime? (I would not be okay with that)

7
  • 6
    Avoiding the bit where you return an invalid pointer (to freed memory) would be even better. Commented Jul 16, 2014 at 14:32
  • @MikeSeymour Good point. It seems like setting the return type to char const* is just not practical Commented Jul 16, 2014 at 14:46
  • std::string uses dynamic memory so I can think of no reason for doing this at compile type, especially not for performance. Commented Jul 16, 2014 at 16:50
  • @NeilKirk the point is that I don't need to use std::string Commented Jul 16, 2014 at 17:39
  • Having a real compile-time C++ string type would be a great tool to have in the standard, and it's feasible, but it hasn't happened yet. This recent proposal is interesting, though. Commented Jul 16, 2014 at 22:09

4 Answers 4

5

You could use something like this. Everything happens at compile time. Specialize base_typename_struct to define your primitive types.

template <const char* str, int len, char... suffix> struct append { static constexpr const char* value() { return append<str, len-1, str[len-1], suffix...>::value(); } }; template <const char* str, char... suffix> struct append<str, 0, suffix...> { static const char value_str[]; static constexpr const char* value() { return value_str; } }; template <const char* str, char... suffix> const char append<str, 0, suffix...>::value_str[] = { suffix..., 0 }; template <typename T> struct base_typename_struct; template <> struct base_typename_struct<int> { static constexpr const char name[] = "int"; }; template <typename T, char... suffix> struct typename_struct { typedef base_typename_struct<T> base; static const char* name() { return append<base::name, sizeof(base::name)-1, suffix...>::value(); } }; template <typename T, char... suffix> struct typename_struct<T*, suffix...>: public typename_struct<T, '*', suffix...> { }; int main() { cout << typename_struct<int****>::name() << endl; } 
Sign up to request clarification or add additional context in comments.

10 Comments

I think there may be a way to automatically turn int into 'i','n','t' using a macro. Thanks
This is awesome, but I think it is limited by not being able to concatenate constexpr strings, it only allows for appending single char's. Any ideas for making it slightly more powerful so I can do arbitrary concats? Marco's link seems like a lead. I do believe it's possible to have a macro that fetches (compile time of course) a sub index of a char* string... what we need is something that takes "int" into 'i','n','t'
The specific use case would be e.g. obtaining the string "int* Array" out of the type int*[]
@StevenLu, You can do it with a macro: EXPAND("int") => 'i', 'n', 't'. Using BOOST_PP_REPEAT with some sort of PushBack<str, c> does the trick. Elaboration
With generalized constexpr functions, it becomes more feasible to use a constexpr class that returns new instances upon modification (a la pure functions).
|
5

Alternative way without using recursive templates (but requires C++14):

#include <utility> template<int...I> using is = std::integer_sequence<int,I...>; template<int N> using make_is = std::make_integer_sequence<int,N>; constexpr auto size(const char*s) { int i = 0; while(*s!=0){++i;++s;} return i; } template<const char*, typename, const char*, typename> struct concat_impl; template<const char* S1, int... I1, const char* S2, int... I2> struct concat_impl<S1, is<I1...>, S2, is<I2...>> { static constexpr const char value[] { S1[I1]..., S2[I2]..., 0 }; }; template<const char* S1, const char* S2> constexpr auto concat { concat_impl<S1, make_is<size(S1)>, S2, make_is<size(S2)>>::value }; 

Example:

constexpr const char a[] = "int"; constexpr const char c[] = "**"; #include <iostream> int main() { std::cout << concat<a,b> << '\n'; } 

append characters to string can also be implemented like this, by replacing the second const char* parameter with char....

1 Comment

If I try constexpr auto a = "123"; constexpr auto b = "456"; constexpr auto c = concat<a, b>; I get an error. Any clue why?
0

I'm not sure of what you're searching for but I believe you're interested in a combination of typeid and name-demangling (which compiler are you using?)

In gcc it would be something like

#include<iostream> #include <string> #include <typeinfo> #include <cstdlib> #include <memory> #include <cxxabi.h> using namespace std; std::string demangle(const char* name) { int status = -4; // some arbitrary value to eliminate the compiler warning // enable c++11 by passing the flag -std=c++11 to g++ std::unique_ptr<char, void(*)(void*)> res { abi::__cxa_demangle(name, NULL, NULL, &status), std::free }; return (status==0) ? res.get() : name ; } template <typename T> struct typename_struct { static std::string name() { std::string typeName = typeid(T).name(); return demangle(typeName.c_str()); } }; int main(){ cout << typename_struct<int****>::name(); // Prints "int****" return 0; } 

http://ideone.com/nLsFF0

Sources: https://stackoverflow.com/a/4541470/1938163

As for your question: those aren't constexpr constructs, thus the evaluation happens at runtime although the templated parameters and code are instantiated at compile-time.

Using templates doesn't mean every instruction contained in there will be executed and resolved at "compile-time".

I believe you can't achieve this whole bunch of stuff at compile-time since there are de-mangling functions (ABI-specific) involved. If I interpreted your question wrong please let me know.

5 Comments

Right, I know about typeid and name-demangling, I am doing that in parallel (so I can check against it -- it normally makes template instantiations' types ugly). My project here goes about achieving "reflection" in a different way, so I'm trying to see if I can define templates to cover all the bases. It's been working well so far. Now you say they are not constexpr constructs, is it because I'm using std::string to do the concatenation? Is there a way for me to force the concatenation to happen at compiletime if I can make all these templates also constexprs (if that even makes any sense)?
It's actually not a great big deal if each "pointer-chain" induces a linear-time string concatenation operation at runtime. Mainly because I only need this for "compatibility". Use of these insane types is ill-advised in almost all cases.
@StevenLu There are several constraints for constexpr functions, take a look here: en.cppreference.com/w/cpp/language/constexpr . This won't work with std::string, you might want to also take a look here for string concatenation: stackoverflow.com/a/9054709/1938163
@StevenLu: You can't perform reflection in a useful way without compiler support.
@ Marco Awesome!!!! Thanks a lot for the links. @Puppy Well I'm not done yet, but when I can show you the evidence for it, I will. C++11 has a nice bag of tricks. It may be sufficient. (Hint. Epic quantities of preprocessor macros and templates)
0
#include <iostream> // *************************************************************************** template<const char* S1, const char* S2, size_t I1 = 0, size_t I2 = 0, char = S1[I1], char = S2[I2], char... Chars> struct Concat : Concat<S1, S2, I1 + 1, I2, S1[I1 + 1], S2[I2], Chars..., S1[I1]> { }; // **************************************************************************** template<const char* S1, const char* S2, size_t I1, size_t I2, char C2, char... Chars> struct Concat<S1, S2, I1, I2, 0, C2, Chars...> : Concat<S1, S2, I1, I2 + 1, 0, S2[I2 + 1], Chars..., S2[I2]> { }; // **************************************************************************** template<const char* S1, const char* S2, size_t N1, size_t N2, char... Chars> struct Concat<S1, S2, N1, N2, 0, 0, Chars...> { static constexpr const char Text[] = { Chars... , 0 }; }; // **************************************************************************** static constexpr const char A[] = "123"; static constexpr const char B[] = "456"; // **************************************************************************** int main(int argc, char* argv[]){ std::cout << Concat<A, B>::Text << std::endl; return 0; } 

1 Comment

Cool! This actually returns a string literal.I have a template with which I can test and your Concat<A, B>::Text returns true, meaning that the result is a new string literal so the strings are concatenated at compile time. :)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.