2

Some langauges contain a construct to express immutable symbols. E.g., in Ruby symbol literals have the form: :symbolName. Then it is, e.g., possible to use them to efficiently retrieve a value from a map (error_count[:syntax_errors]) and moreover they can be easily converted into strings (:syntax_error.to_s). My experience is that this creates very well readable and maintainable code.

Is there something similar available in C++ (I don't want want to use integer constants because I need to declare them and they cannot be easily converted into strings; I don't want to run a script over my source files prior to compilation which does some fancy substitutions)? I am looking for a solution using the C preprocessor or some tricks from meta template programming.

user2683038
  • 667
  • 6
  • 17
  • 1
    You're thinking of an enumerated type. Unfortunately, in C++, they require a bit of boilerplate code if you plan on converting them into strings. – Trevor Hickey Aug 23 '14 at 05:08
  • Yes, I don't want to use enums either. I want to declare and write as less as possible. – user2683038 Aug 23 '14 at 05:09
  • I found this: http://www.comeaucomputing.com/techtalk/templates/#stringliteral which will help when all things-to-be-symbols are `const`, but AFAIK that limits you to symbols you type in. No `"foo".to_sym`. However, I think there's a way to combine the two methods. Let me get back to you on this. – Narfanator Feb 26 '16 at 22:34
  • To be clear, the desired functionality is: Some way to turn string literals into "symbols", some way to turn run-time strings into "symbols", some way to turn "symbols" back into their strings. Maximize the interoperability with existing C++ stuff, the performance/optimizability - which is the point of symbols in the first place. – Narfanator Feb 26 '16 at 22:36

3 Answers3

4

There is a std::unordered_map<> in C++11, but since you are talking about "symbol literals" you can actually do this much better with static symbols. The simples way is with straight C and the C preprocessor.

#define declare_literal(x) const char x[] = #x
declare_literal(foo); //now "foo" is a global symbol you can use everywhere

That doesn't provide the single instance guarantee you might be looking for. That is actually trickier to accomplish than one might hope, but you can do it with some ugliness:

template <typename CharType, CharType... args>
struct symbol_base {
    using self_type = symbol<CharType, args...>;
    static constexpr std::size_t char_length = sizeof...(args) / sizeof(CharType);
    static constexpr CharType value[char_length] = {CharType(args)...};
    static const std::string& as_string() { static const std::basic_string<CharType> val{value, char_length}; return val; }
    operator const std::string&() const { return as_string(); }
};

template <char... args>
using symbol = symbol_base<char, args...>;

template <wchar_t... args>
using wsymbol = symbol_base<wchar_t, args...>;

So then you can make a symbol for "foo" with:

symbol<'f', 'o', 'o'> foo;

Not exactly elegant, but effective.

Christopher Smith
  • 5,372
  • 1
  • 34
  • 18
2

There are no symbols in C++, because compile time and run time are different worlds.

You could however play some preprocessor tricks (see X_macros). See this, this, etc...

So you might have a file defining your symbols like e.g.

 // file my-symbols.def
 MY_SYMBOL(a)
 MY_SYMBOL(foo)
 #undef MY_SYMBOL

and you would use it several times, e.g. once to declare some enum:

enum my_symbols_en {
#define MY_SYMBOL(N) sy_##N,
#include "my-symbols.def"
};

If you don't like enum-s use a similar trick to declare something different, e.g. static instances...

and once to e.g. define the output routine

std::ostream& operator <<(std::ostream& out, my_symbols_en symb) {
  switch (symb) {
#define MY_SYMBOL(Sy) case sy_##N: out << #Sy ; break;
#include "my-symbols.def"
  default: std::cerr << "bad symbol" << std::endl; exit(EXIT_FAILURE);
  }
  return out;
}

and likewise for input, hashing, string-conversion, etc, etc...

You could also use specialized C++ generating scripts in your build framework. For instance, the my-symbols.def might be generated by some awk script...

Community
  • 1
  • 1
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
1

The enum construct is built for this.

Some good example for using enums

If you would like a hash map, consider std::unordered_map

Simple hashmap implementation in C++

Community
  • 1
  • 1
Douglas Lovell
  • 1,549
  • 17
  • 27
  • I don't want to use enums, I am looking for something that comes close to the Ruby style. – user2683038 Aug 23 '14 at 05:12
  • 1
    There's no `std::hash_map` in the standard library. – quantdev Aug 23 '14 at 05:14
  • I know that it is possible to do amazing things using CPP and template meta programming (e.g., in the boost library). I am wondering whether anyone has ever applied those concepts to this problem. – user2683038 Aug 23 '14 at 05:17
  • apology there, @quanddev std::unordered_map http://en.cppreference.com/w/cpp/container/unordered_map – Douglas Lovell Aug 23 '14 at 05:17
  • Symbols exist in Ruby for an efficiency issue that C++ doesn't have. If you want to reference the same thing in different places, without making copies of that thing, use pointers. – Trevor Hickey Aug 23 '14 at 05:33