5

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)

Steven Lu
  • 41,389
  • 58
  • 210
  • 364
  • 6
    Avoiding the bit where you return an invalid pointer (to freed memory) would be even better. – Mike Seymour Jul 16 '14 at 14:32
  • @MikeSeymour Good point. It seems like setting the return type to `char const*` is just not practical – Steven Lu Jul 16 '14 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. – Neil Kirk Jul 16 '14 at 16:50
  • @NeilKirk the point is that I don't need to use `std::string` – Steven Lu Jul 16 '14 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](https://isocpp.org/blog/2014/07/n4121-compile-time-string-stdstring-literalltngt-andrew-tomazos) is interesting, though. – scry Jul 16 '14 at 22:09
  • Also see discussion [here](http://stackoverflow.com/questions/15858141/conveniently-declaring-compile-time-strings-in-c). – scry Jul 16 '14 at 22:11
  • Check [this](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4121.pdf) proposal, which unfortunately did not make it in C++17. Regardless, it is quite easy to implement in C++11/14. – sbabbi Oct 24 '16 at 12:16

4 Answers4

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;
}
pdw
  • 8,359
  • 2
  • 29
  • 41
  • I think there may be a way to automatically turn `int` into `'i','n','t'` using a macro. Thanks – Steven Lu Jul 16 '14 at 15:19
  • 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](http://stackoverflow.com/a/9054709/1938163) 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'` – Steven Lu Jul 16 '14 at 17:59
  • The specific use case would be e.g. obtaining the string "int* Array" out of the type `int*[]` – Steven Lu Jul 16 '14 at 18:01
  • @StevenLu, You can do it with a macro: `EXPAND("int") => 'i', 'n', 't'`. Using `BOOST_PP_REPEAT` with some sort of `PushBack` does the trick. [Elaboration](http://web.archive.org/web/20130930081424/http://cpp-next.com/archive/2012/10/using-strings-in-c-template-metaprograms/) – chris Jul 16 '14 at 18:10
  • With generalized `constexpr` functions, it becomes more feasible to use a `constexpr` class that returns new instances upon modification (a la pure functions). – chris Jul 16 '14 at 18:16
  • @StevenLu, maybe look at Sprout? Looks like it has a compile-time std::string work-alike. Looks very comprehensive. Mentioned in the other answer of Marco's link. http://stackoverflow.com/a/9073636/2298336 – pdw Jul 16 '14 at 19:37
  • Quite the big can of worms I opened. Thanks for all the links everyone. Enough stuff to wade through for weeks here. Worst part is, my code already works! It just doesn't have compile-time strings for the extra final bit of speed. So I can barely justify going down this rabbit hole. Still, Sprout actually looks really good. – Steven Lu Jul 16 '14 at 20:34
  • Wondering if the above code is standard - Visual Studio 2015 spits the following: `error C4576: a parenthesized type followed by an initializer list is a non-standard explicit type conversion syntax` and refuses to compile. – GaspardP Oct 22 '16 at 01:14
  • Ah!, you can make it work in Visual Studio 2015 by having a static `static const char value_str[];` initialized with `{ suffix..., 0 }`, and `return value_str` in `value()` – GaspardP Oct 22 '16 at 01:27
  • Yeah, that was a GCC extension. I edited the code. I was surprised to get a comment on an old answer about C++ esoterica :) – pdw Oct 24 '16 at 12:11
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....

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Hedede
  • 1,133
  • 13
  • 27
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.

Community
  • 1
  • 1
Marco A.
  • 43,032
  • 26
  • 132
  • 246
  • 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)? – Steven Lu Jul 16 '14 at 14:57
  • 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. – Steven Lu Jul 16 '14 at 15:00
  • @StevenLu There are several constraints for `constexpr` functions, take a look here: http://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: http://stackoverflow.com/a/9054709/1938163 – Marco A. Jul 16 '14 at 15:02
  • @StevenLu: You can't perform reflection in a useful way without compiler support. – Puppy Jul 16 '14 at 15:05
  • @ 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) – Steven Lu Jul 16 '14 at 15:07
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;
}
Cooler
  • 19
  • 3