4

I have some SQL queries with binds in my C++ code, those queries are static const std::string, because those queries are complex it is very easy to be wrong with some details. I would like to do some very basic checks in compilation time, for example counting the number of commas or : character.

3
  • You can achieve that with constexpr functions and metaprogramming basically Commented Sep 4, 2018 at 8:06
  • Instead of bending over backwards to get this done at compile time (presumably because you are afraid of the impact these checks might have on performance), may I suggest two commonly employed alternatives? 1) Make those checks conditionally-compiled for debug versions of your application only (#ifndef NDEBUG ...). You get the checks during development, without any impact on release performance. 2) Measure, optimize, measure. Compared with a SQL connect / query, some string checking is most likely taking a tiny, negligible amount of time. You might not even have a performance problem. Commented Sep 4, 2018 at 8:29
  • @DevSolar I think early error checking is the real reason to parse at compile time, not performance. The OP actually said so: "I would like to do some very basic checks in compilation time" Commented Sep 4, 2018 at 13:19

4 Answers 4

5

You can't. A static const std::string doesn't exist at compile time.

String literals are possible with constexpr functions, but not std::string objects.

Sign up to request clarification or add additional context in comments.

Comments

4

You can't parse std::string at compile time, because it can be constructed only at run-time. But there are nice answers at StackOverflow that describes how to define and manipulate compile-time strings:

  1. Compile time string encryption using constexpr.
  2. Conveniently Declaring Compile-Time Strings in C++

They refer to of Scott Schurr's str_const starting at page 29:

class str_const { // constexpr string
private:
  const char* const p_;
  const std::size_t sz_;
public:
  template<std::size_t N>
  constexpr str_const(const char(&a)[N]) : // ctor
    p_(a), sz_(N-1) {}
  constexpr char operator[](std::size_t n) { // []
    return n < sz_ ? p_[n] : throw std::out_of_range("");
  }
  constexpr std::size_t size() { return sz_; } // size()
};

See how Jason Turner's constexpr JSON parser works. It is able to parse a whole JSON string at compile time, so it should be possible to parse and validate SQL at compile time. You just need to use Scott's std_const, or Jason's static_string for that.

Here is a trivial extension that makes it play nicer with std::string_view, and have a compile-time substr method:

class str_const {
private:
  const char* const p_;
  const std::size_t sz_;
public:
  template<std::size_t N>
  constexpr str_const(const char(&a)[N]) :
    p_(a), sz_(N-1) {}
  constexpr str_const(const std::string_view & sv) :
    p_(sv.begin()), sz_(sv.size()) {}
  constexpr operator std::string_view() const
  { return {p_, sz_}; }

  constexpr char operator[](std::size_t n) const { // []
    return n < sz_ ? p_[n] : throw std::out_of_range("");
  }
  constexpr std::size_t size() const { return sz_; } // size()
  constexpr const char*c_str() const { return p_; }

  constexpr const char*begin() const { return p_; }
  constexpr const char*end() const { return p_ + sz_; }
  constexpr str_const substr(unsigned from, unsigned size) const
  {
    return from+size <= sz_ ? std::string_view{p_ + from, size} : throw std::out_of_range("");
  }
};
std::ostream & operator<<(std::ostream& out, str_const str) {
   return out << std::string_view(str);
}

Comments

2

std::string doesn't exist at compile time. If you want to have such a behavior, you can use the string literals with constexpr as below:

constexpr const char* const var = "string";

To understand more about this, please see the assemble code generated for this:

#include <string>

int main()
{
    constexpr const char* const str = "string";
    const std::string test = "test";
}

With X86-64 Clang 6.0.0 compiler and with 0 optimizations,

constexpr const char* const str = "string";

generated below code:

subq    $80, %rsp
movq    $.L.str, -8(%rbp)
leaq    -48(%rbp), %rax

.L.str:
        .asciz  "string"

and for const std::string test = "test"; below code is generated (Just a snippet) So it calls the std::allocater which allocates the memory on the heap and then constructs the string object.

        movq    %rax, %rdi
        movq    %rax, -72(%rbp)         # 8-byte Spill
        callq   std::allocator<char>::allocator() [complete object constructor]
        movl    $.L.str.1, %ecx
        movl    %ecx, %esi
        leaq    -40(%rbp), %rdi
        movq    -72(%rbp), %rdx         # 8-byte Reload
        callq   std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&)
        jmp     .LBB0_1

1 Comment

if std::string is short enough for SSO, can't static const std::string still be a compile time object?
0

As Sebastian already mentioned, you can't if you need std::string. But maybe, as an alternative, you can do something like constexpr auto query = "MY SQL QUERY"; I don't know if you are allowed to modify the type of the queries. Then at runtime, query can be used to construct a std::string if you need one. Checks at compile time can also be done.

The disadvantage of course is that it is copied at runtime when creating a std::string.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.