You've essentially stumbled upon the problem of creating a function to return polymorphic containers, without giving up type safety. Raw types may seem like an easy , type-unsafe way out, but let's simplify the problem and see if a better solution exists.
Imagine a function that returns an empty list. Now, an empty list can be safely assigned to any std::list<T>, for all T. Because it's empty! Would you use raw types there? How about this instead-
It's type safe, and you didn't need "raw types"! This is one of the fundamental concepts in type theory - the forall quantification. In Haskell, it's equivalent to-
nil::foralla.[a]nil=[]
Which essentially reads- "The type of nil is list of a for all a".
I suggest doing something similar for your usecase. Make your parse function accept a type parameter.
Addendum: You're not gonna get the awesomesauce type inference stuff in C++ with this. In that example, you'll have to specify the type parameter to the nil call even if you already have a typed left hand side. But I'm assuming you're more interested in type safety than ease of use. If you're looking for both, hindley-milner is that way :)
I'm Calin Baenen – AKA KattyTheEnby – a programmer born October 30th, 2006.
I love programming, it has been my passion since I was a kid, and will forever be my passion.
Could you provide an example of how your example helps? I still think I need raw types, because the goal is to have multiples match up, so that Token<int, 2> can also be grouped with a Token<std::string, "hello">. - With raw types, I could just say Token, and I would only have to figure out what type is being used (which isn't too hard in C++).
Ah, you want a heterogenous array, not a polymorphic one. If it's not at all possible to design your API in a way to need heterogenous arrays - your only option is to use std::variant. You can't have just Token though, since that immediately kills static typing.
You'll most likely need to throw in a whole bunch of holds_alternative checks before you can actually use the value though. Yeah, I know it's painful - but that's not an inherent drawback of type safe static typing, it's just C++.
I'm Calin Baenen – AKA KattyTheEnby – a programmer born October 30th, 2006.
I love programming, it has been my passion since I was a kid, and will forever be my passion.
but that's not an inherent drawback of type safe static typing, it's just C++.
C++ is my favorite language, but in this regard, it treats things very stupidly.
Sure, Token (kind of) kills static typing, but you must admit, for the purpose of having a flexible array (or any structure), allowing raw types would be nice as an option. -- Or, at least make it easier to reach the end goal for something like this.
(Maybe I'll just make my own heterogeneous array implementation, if that's considered "okay" by most people's logic.)
That's haskell, but it should be readable regardless. Notice how the raw token value is the std::variant, and the Token is a wrapper around it. The T that you pass to your Token template is not present here, because it doesn't need to be present. TokenValue is actually a tagged union. std::variant is a really roundabout and overly complex way of doing tagged unions - so it's equivalent.
I really don't think you'll ever need to track the T that you use for the value field. It should just be tracked by the tagged union (since it's a runtime concept).
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
You've essentially stumbled upon the problem of creating a function to return polymorphic containers, without giving up type safety. Raw types may seem like an easy , type-unsafe way out, but let's simplify the problem and see if a better solution exists.
Imagine a function that returns an empty list. Now, an empty list can be safely assigned to any
std::list<T>, for allT. Because it's empty! Would you use raw types there? How about this instead-It's type safe, and you didn't need "raw types"! This is one of the fundamental concepts in type theory - the
forallquantification. In Haskell, it's equivalent to-Which essentially reads- "The type of
nilislist of afor alla".I suggest doing something similar for your usecase. Make your
parsefunction accept a type parameter.Addendum: You're not gonna get the awesomesauce type inference stuff in C++ with this. In that example, you'll have to specify the type parameter to the
nilcall even if you already have a typed left hand side. But I'm assuming you're more interested in type safety than ease of use. If you're looking for both, hindley-milner is that way :)Could you provide an example of how your example helps? I still think I need raw types, because the goal is to have multiples match up, so that
Token<int, 2>can also be grouped with aToken<std::string, "hello">. - With raw types, I could just sayToken, and I would only have to figure out what type is being used (which isn't too hard in C++).Ah, you want a heterogenous array, not a polymorphic one. If it's not at all possible to design your API in a way to need heterogenous arrays - your only option is to use
std::variant. You can't have justTokenthough, since that immediately kills static typing.You'll most likely need to throw in a whole bunch of
holds_alternativechecks before you can actually use the value though. Yeah, I know it's painful - but that's not an inherent drawback of type safe static typing, it's just C++.C++ is my favorite language, but in this regard, it treats things very stupidly.
Sure,
Token(kind of) kills static typing, but you must admit, for the purpose of having a flexible array (or any structure), allowing raw types would be nice as an option. -- Or, at least make it easier to reach the end goal for something like this.(Maybe I'll just make my own heterogeneous array implementation, if that's considered "okay" by most people's logic.)
Also, regarding an API redesign from my previous reply. This is what I generally see token types implemented as, for parsers/lexers-
That's haskell, but it should be readable regardless. Notice how the raw token value is the
std::variant, and theTokenis a wrapper around it. TheTthat you pass to yourTokentemplate is not present here, because it doesn't need to be present.TokenValueis actually a tagged union.std::variantis a really roundabout and overly complex way of doing tagged unions - so it's equivalent.I really don't think you'll ever need to track the
Tthat you use for thevaluefield. It should just be tracked by the tagged union (since it's a runtime concept).