-4

Suppose I have a function that takes a string as input:

SomeOutputType f_impl(const char* s);

Most call sites just use string literals as input, e.g. f("Hello, world"). Suppose I have implemented the following function to compute the result at compile time

template <char...> SomeOutputType f_impl();

My question is, is there a way to let the call sites like f("Hello, world") calls the templated form, while for general call sites like string s="Hello, world"; f(s.c_str()); calls the general form? For clarification, auto s = "Hello, world"; f(s); don't have to call the templated form because s is now a variable and no longer a compile time constant.

A useful case for this question is to optimize printf. In most cases the format will be string literals so a lot of things can be done at compile time to optimize things, instead of parsing the format at runtime.

Kan Li
  • 8,557
  • 8
  • 53
  • 93
  • 1
    Your function template has no input argument. Is that intended? – Andy Prowl Feb 17 '13 at 20:23
  • Yes, the string literals should be converted to the template paramters. – Kan Li Feb 17 '13 at 20:23
  • 1
    You can't invoke a function uniformly if you have to supply a *function* argument in one case and a *template* argument in another case – Andy Prowl Feb 17 '13 at 20:25
  • Well I modified my question so that it reflects my intension clearly. Users call function `f` and it somehow dispatches to different forms of `f_impl` according to whether the call site uses string literal or not. – Kan Li Feb 17 '13 at 20:31
  • So basically you want to specialise a template on whether a function argument was a string literal? – Lightness Races in Orbit Feb 17 '13 at 21:12
  • @icando I had read your question and I still am interested to know why you need this. I was assuming you're not a standard library implementor and are not writing `printf` yourself. – Peter Wood Feb 17 '13 at 22:41
  • @PeterWood, I am actually trying to implement printf (for my own use, not to publish to any standard library). I want it be type-safe so I uses varadic template, so that I can do type checks. Also I want to check the `format` string if it is given as a literal. – Kan Li Feb 17 '13 at 22:48

3 Answers3

17

No, a string literal like "foo" has the type const char[S + 1] where S is the number of characters you wrote. It behaves like an array of that type with no special rules.

In C++03, there was a special rule that said that a string literal could convert to char*. That allowed you to say

#define isStringLiteral(X) \
  isConvertibleToCharStar(X) && hasTypeConstCharArray(X)

For example isStringLiteral(+"foo") would yield false, and isStringLiteral("foo") would yield true. Even this possibiliy would not have allowed you to call a function with a string literal argument and behave differently.

C++11 removed that special conversion rule and string literals behave like any other arrays. In C++11 as a dirty hack you can compose some macros, matching some simple string literals without handling escape sequences

constexpr bool isStringLiteral(const char *x, int n = 0) {
  return *x == '"' ? 
           n == 0 ?
             isStringLiteral(x + 1, n + 1)
             : !*(x + 1) 
           : (*x && n != 0 && isStringLiteral(x + 1, n + 1));
}

#define FastFun(X) \
  (isStringLiteral(#X) ? fConstExpr(X, sizeof(X) - 1) : f(X))
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • I not only need to know if the input is string literal, but also need to know the content at compile time. – Kan Li Feb 17 '13 at 20:28
  • @icando you can know the contents at compile time. But you cannot know that what was given is a string literal. – Johannes Schaub - litb Feb 17 '13 at 20:28
  • How to know the content at compile time? – Kan Li Feb 17 '13 at 20:29
  • well, looks like you are mislead by my title, I apologize. I don't just want a function to tell me if something is string literal or not, but I also need to compute something based on the string literal's content at compile. For example, I want to write a function to compute the sum of all characters in the string literal at compile time. – Kan Li Feb 17 '13 at 20:58
  • @icando i am not mislead, you don't need to worry. – Johannes Schaub - litb Feb 17 '13 at 21:05
  • ok, if you were not mislead, then I have to say your answer is totally useless for my question here. – Kan Li Feb 17 '13 at 21:07
  • 4
    @icando don't worry. it may be useful to others. – Johannes Schaub - litb Feb 17 '13 at 21:08
  • Then please delete it, it is totally irrelevant to my question here. – Kan Li Feb 17 '13 at 21:09
  • 12
    @icando no, I won't delete it. It correctly explains why it is not possible to do what you asked for. I can understand that you find it frustrating that it won't work, but having no answer instead of an answer that states it doesn't work won't change it. – Johannes Schaub - litb Feb 17 '13 at 21:10
  • `In C++03, there was a special rule that said that a string literal could convert to char*` Erm.. really? I thought that you could initialise a `const char*` or a `char[n]` from a string literal (where `n` may be omitted), but never `char*` to be compliant. – Lightness Races in Orbit Feb 17 '13 at 21:12
  • @JohannesSchaub, you are so wrong. It is possible. I actually have a hacky answer in my mind, and I am looking for an elegant way to do it. – Kan Li Feb 17 '13 at 21:12
  • @LightnessRacesinOrbit surprise! :) It's item #3 in http://stackoverflow.com/a/6473250/34509 – Johannes Schaub - litb Feb 17 '13 at 21:17
  • @JohannesSchaub-litb: o.O Then why has GCC _always_ warned that this is non-compliant? _\[edit: correction, [deprecated](http://codepad.org/IFlCyQkm) - guess that explains that\]_ – Lightness Races in Orbit Feb 17 '13 at 21:20
  • @JohannesSchaub-litb, your answer doesn't make any sense. It can't even pass the compile. – Kan Li Feb 17 '13 at 22:02
  • 4
    @icando: Johannes Schaub is a world-reknowned _expert_ in C++ who has contributed significant expertise to the standards committee itself, so you might want to spend a little less time blaming everybody for doing everything wrong, and a little more time in trying to figure out what it is that _you_ are doing wrong. – Lightness Races in Orbit Feb 17 '13 at 23:12
  • @LightnessRacesinOrbit, now I gained more confidence over myself now. If such a person is a C++ expert, then I can definitely call myself C++ super expert now. It also explains why C++ is evolving so slowly and losing its popularity. It is because standard committee is filled by those non-creative guys. Actually C++ itself is an art, and lots of powerful paradigms are found by programmers, not by the committee guys. – Kan Li Feb 17 '13 at 23:22
  • 6
    @icando: Yes, that's exactly right. You are "C++ super expert". – Lightness Races in Orbit Feb 17 '13 at 23:29
  • @EtiennedeMartel, I am not anti-committee, I just want to kick this stupid answer out of my question here. A guy ``has contributed significant expertise...'' doesn't mean everything he said is correct. Whether an answer is correct or incorrect is just a matter of fact, irrelevant to who said it, even if he was smart in the past. – Kan Li Feb 17 '13 at 23:49
  • 1
    @icando: That is true - there is no direct logical equivalence and one does not prove the other. However, regardless, this answer _is_ correct and there's no way out of that one. – Lightness Races in Orbit Feb 17 '13 at 23:53
  • 2
    @icando Absolutely, appealing to authority is a weak argument. How about letting the voters decide? – Peter Wood Feb 17 '13 at 23:54
  • 1
    @icando It's not stupid if it's the truth, even if said truth is inconvenient for you. – Etienne de Martel Feb 17 '13 at 23:56
  • 1
    @icando: You know, you *can* actually operate on a string at compile-time - there's `constexpr`. Doesn't change the fact that you *can not* find out if said string was a literal or not. There is just *no* way in standard C++. – Xeo Feb 17 '13 at 23:58
  • 4
    @icando I am now invoking [Godwin's Law](http://en.wikipedia.org/wiki/Godwin's_law). Discussion over; you lost. – Peter Wood Feb 18 '13 at 00:01
  • @PeterWood, I never called for discussion. I was only ASKING questions. So anyone who is out-of-imagination is out of my consideration. Win/lose only happens to those guys who are really smart and I consider as rivals. – Kan Li Feb 18 '13 at 00:11
  • 3
    I dunno. This guy could be great. He should start a sect. It would be Super Effective – sehe Feb 18 '13 at 00:14
  • @sehe: Suddenly, a wild smattering of OMG WTF appears. – Lightness Races in Orbit Feb 18 '13 at 00:15
0

While I haven't tested this, I think if you just declare the function constexpr and compile with high optimization, the compiler will compute at compile time whenever possible. As a bonus, you don't need to write the code twice. On the other hand, you have to write it once in constexpr style.

dspeyer
  • 2,904
  • 1
  • 18
  • 24
-2

If I understand the question correctly, I actually think something like this is possible using a function overload. Here's an article that shows the basic idea. In your case I think it would be sufficient to have the following two overloads:

void f(char const *);

template<unsigned int N>
void f(char const (&)[N]);

The latter should be invoked when the string is a string literal, the latter at other times. If the compiler is sufficiently good at optimizing then calls to the latter may be evaluated at compile time.

EDIT:

Alright, it bothered me that the above solution didn't work, so I did some playing around and I think I came up with a solution:

#include <string>
#include <boost/utility/enable_if.hpp>

template<typename T>
struct is_string_literal {
  enum { value = false };
};

template<unsigned int N>
struct is_string_literal<char const (&)[N]> {
   enum { value = true };
};

template<typename T>
typename boost::disable_if<is_string_literal<T> >::type
foo(T) {
  std::cout << "foo1" << std::endl;
}

template<int N>
void foo(char const (&)[N]) {
  std::cout << "foo2" << std::endl;
}

int main( ) {
  std::string bar = "blah";
  char const str[] = "blah";

  foo(str);
  foo("blah");

  foo(bar.data());
}

The output (on GCC 4.4 with -O3) is:

foo2
foo2
foo1

I admit that I don't completely understand why this works when the previous solution didn't. Maybe there's something about overload resolution that I don't completely understand.

Chris Hayden
  • 1,104
  • 6
  • 6
  • The non-template function is always a better match than template functions (try out the code and see which one is chosen). – Jesse Good Feb 17 '13 at 20:49
  • Well, this still relies on the optimization of the compiler and only works for simple `f_impl`. I don't think for functions as complicated as `printf` this technique helps. – Kan Li Feb 17 '13 at 20:51
  • @Jesse Seems you're right. It does beg the question of how the author got the results he did in the post. – Chris Hayden Feb 17 '13 at 20:57
  • @icando I don't understand. Any solution will rely on the optimizing capabilities of the compiler to evaluate f at compile time unless you can represent it as a constexpr in C++11. – Chris Hayden Feb 17 '13 at 20:58
  • @ChrisHayden, if my templated version is called, then I only assume compiler can do basic inlines. The technique proposed here requires on more aggressive optimizations that unroll the loop of unknown iterations, e.g. in `printf`, most implementations just `while` loop through the format string. I can't assume compilers can unroll the `while` loop in this case. – Kan Li Feb 17 '13 at 21:03
  • @ChrisHayden: The author of that post is not using templates, s(he) is using the preprocessor to define a bunch of functions like `void f(char const (&)[1]); void f(char const (&)[2]);`, etc. – Jesse Good Feb 17 '13 at 21:04
  • @Jesse Sure, but they are also defining a constructor that accepts a char const *. If that really is a better match for string literals then I would expect it do be invoked by overload resolution, not the versions that accept the character arrays. Also, scroll to the bottom where he discusses a template method. – Chris Hayden Feb 17 '13 at 21:18
  • @icando I still don't see your point. The best you can do is get the compiler to detect whether you are dealing with a string literal. Whether it's capable of unrolling the loop is a question of the compilers optimizing capabilities no matter how you perform the detection. The only exception is if you use preprocessor unrolling, as shown in the link. – Chris Hayden Feb 17 '13 at 21:21
  • @ChrisHayden: The key point your are missing is in my first comment. It's **template** functions vs. **non-template** functions, your solution works because the foo overloads are *both* **templates**. If you add a **non-template** `foo(const char*)` it will be preferred over the templated versions. – Jesse Good Feb 17 '13 at 21:48
  • @Jesse I understand what you're saying, and I admit that I don't know the specific overload resolution rule that causes that to happen. But, it doesn't really matter, does it? In this case the OP wants to be able to detect string literals and invoke an appropriate function. My solution achieves that. – Chris Hayden Feb 17 '13 at 22:13
  • 1
    You're so wrong, it's not even funny. Your "Is string literal" template function will be called for *any* array of characters, string literal or not. – Puppy Feb 17 '13 at 23:40
  • It will be called for any array of const characters where the number of elements in the array can be determined statically. You're right in pointing out that the array may not be initialized as a string literal, but I don't see why that matters. Why would you not want to call the function that offers the best chance for optimization? – Chris Hayden Feb 18 '13 at 12:23
  • Because the OP clearly specified "String literals only". That is not the same as "Primitive array whose size can be statically determined". – Puppy Feb 19 '13 at 22:18