Type-safe inline object definition.

131 views
Skip to first unread message

Sarfaraz Nawaz

unread,
Dec 24, 2017, 7:22:28 AM12/24/17
to ISO C++ Standard - Future Proposals

C++11 introduced the keyword auto which demonstrates a very simple concept or idea... and that is, if you have a value, then you can create an object out of it whose type will be inferred by the compiler.

auto x = 10;        //x is inferred to be int
auto y = "Hello"s;  //y is inferred to be std::string
auto z = true;      //z is inferred to be bool


That is simple, isn't it? But can we extend this simple idea to create objects of complex/composite types with many attributes/components? How about creating an object with 3 components, name and age, and let the compiler infer (and define) a suitable type to represent the object?
       
//define object
auto person = {
   
auto const name = "Rafi"s;
   
auto age = 55;
};

//access members
std
::cout << person.name << ", "    //person.name is std::string
         
<< person.age << "\n";    //person.age is int  


From this, the compiler instantiates a class template std::object<> describing person in such a way that programmers could introspect its members, their types and names. Here is an example,


template<typename ... Ms>
void print(std::object<Ms...> const & obj) {
   
//access member-values as: obj.get<Ms>() ...
   
//access member-names as: obj.name<Ms>() ...  OR Ms::name() ....
   
//access member-types as: typename Ms::type ...
 
   
((std::cout << Ms::name() << " = " << obj.get<Ms>() << "  [" << nicefy(typeid(Ms::type)) << "]\n") , .... ); //using C++17 fold-expression
   
   
//which prints
   
//
   
//    name = Rafi [std::string]
   
//    age = 55 [int]

   
//Or stdlib itself could provide utility like:
 
    std
::iterate(obj, [](auto const & attr) { std::cout << attr.name() << " = " << attr.value() << " [" << nicefy(attr.type()) << "]\n"; });
}



We could omit auto and write instead (if that's not too weird to look at):

auto person = {
   
const name = "Rafi"s,
   
&age = value,
   
const & expertise = "Playback Singing"s
};


Previous Works As Libraries:

  • CSON designed by me (only POC) but that requires users to define the labels first to be used in the object definition.

    #include <cson/cson.h++>

    //define the labels first
    CSON_LABEL
    (name);
    CSON_LABEL
    (age);

    auto x = cson(name = "Rafi", age = 55, work = "Singing"); //define object

  • IOD designed by Matthieu Garrigues. It's much more feature-rich library. 

In both these libraries, the users has to define the labels/keys to be used in the object definition. That makes them cumbersome to use.

Nicolas Lesser

unread,
Dec 24, 2017, 9:29:27 AM12/24/17
to ISO C++ Standard - Future Proposals


On Sunday, December 24, 2017 at 1:22:28 PM UTC+1, Sarfaraz Nawaz wrote:

C++11 introduced the keyword auto which demonstrates a very simple concept or idea... and that is, if you have a value, then you can create an object out of it whose type will be inferred by the compiler.

auto x = 10;        //x is inferred to be int
auto y = "Hello"s;  //y is inferred to be std::string
auto z = true;      //z is inferred to be bool


That is simple, isn't it? But can we extend this simple idea to create objects of complex/composite types with many attributes/components? How about creating an object with 3 components, name and age, and let the compiler infer (and define) a suitable type to represent the object?
       
//define object
auto person = {
   
auto const name = "Rafi"s;
   
auto age = 55;
};

//access members
std
::cout << person.name << ", "    //person.name is std::string
         
<< person.age << "\n";    //person.age is int  

We already have this:

 struct {
std::string const name = "Rafi";
int age = 55;
} person;

Although `auto` is not possible in classes because that would complicate implementations by a lot apparently (I heard this somewhere, forgot where).

From this, the compiler instantiates a class template std::object<> describing person in such a way that programmers could introspect its members, their types and names. Here is an example,


template<typename ... Ms>
void print(std::object<Ms...> const & obj) {
   
//access member-values as: obj.get<Ms>() ...
   
//access member-names as: obj.name<Ms>() ...  OR Ms::name() ....
   
//access member-types as: typename Ms::type ...
 
   
((std::cout << Ms::name() << " = " << obj.get<Ms>() << "  [" << nicefy(typeid(Ms::type)) << "]\n") , .... ); //using C++17 fold-expression
   
   
//which prints
   
//
   
//    name = Rafi [std::string]
   
//    age = 55 [int]

   
//Or stdlib itself could provide utility like:
 
    std
::iterate(obj, [](auto const & attr) { std::cout << attr.name() << " = " << attr.value() << " [" << nicefy(attr.type()) << "]\n"; });
}



This will probably be possible using reflection, and thus your proposal will probably conflict with the one from the working group. Two possible ways to access the types from your new type above is not the best idea IMO.

Laurent LA RIZZA

unread,
Dec 24, 2017, 6:16:15 PM12/24/17
to ISO C++ Standard - Future Proposals


Le dimanche 24 décembre 2017 15:29:27 UTC+1, Nicolas Lesser a écrit :
 struct {
std::string const name = "Rafi";
int age = 55;
} person;

Although `auto` is not possible in classes because that would complicate implementations by a lot apparently (I heard this somewhere, forgot where).

Close enough, maybe :

#define AUTO(d, i) decltype(i) d = i

struct {
   AUTO(const name, "Rafi"s);
   AUTO(age, 55);
} person;


José Rodrigo

unread,
Dec 25, 2017, 8:18:30 AM12/25/17
to std-pr...@isocpp.org
Why not allow this instead?


auto person = struct { string name = "Bill"; short age = 25; };

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/92a68b32-4be9-4fb9-b0f6-b071503ed698%40isocpp.org.

Ville Voutilainen

unread,
Dec 25, 2017, 8:24:43 AM12/25/17
to ISO C++ Standard - Future Proposals
On 24 December 2017 at 16:29, Nicolas Lesser <blitz...@gmail.com> wrote:
> Although `auto` is not possible in classes because that would complicate
> implementations by a lot apparently (I heard this somewhere, forgot where).


See http://open-std.org/JTC1/SC22/WG21/docs/papers/2014/n3897.html

Nicol Bolas

unread,
Dec 25, 2017, 10:17:45 AM12/25/17
to ISO C++ Standard - Future Proposals
On Monday, December 25, 2017 at 8:18:30 AM UTC-5, José Rodrigo wrote:
Why not allow this instead?

auto person = struct { string name = "Bill"; short age = 25; };

I'm not a big fan of C/C++'s ability to have a giant, multi-line type declaration followed by a tiny, near-invisible variable name, but at the very least we already have the grammar to do this.

Compared to actual problems that the language has, the difference between that and putting `person` at the end of that struct declaration is exceedingly trivial.

Jan Wilmans

unread,
Dec 25, 2017, 10:49:01 AM12/25/17
to std-pr...@isocpp.org
what would be the benefit over giving the struct a name?

Cleiton Santoia

unread,
Jan 9, 2018, 12:53:10 PM1/9/18
to ISO C++ Standard - Future Proposals
I´ll give my 2 cents:

Lambdas already do something like that, consider the following:

    int x = 10;
    int y = 20;
    std::string u = "test";
    
    auto l = [x, &y, z = u + " expression" ](){}

IDK for sure, but I presume that piece of code will probably be converted to :

int x = 10;
int y = 20;
std::string u = "test";

class _some_compiler_gen_weired_name {
 
const int x;  // param#1 'x' captured by copy
 
const int &y; // param#2 '&y' captured by ref  
 
const std::string z; // param#3 'u + " expression"' captured by value
public:
   
_some_compiler_gen_weired_name (int _p1, int& _p2, std::string _p3 ) :
       x
(_p1), y(_p2), z(_p3) {}
};

auto l = _some_compiler_gen_weired_name (x, y, u + " expression");

So, if we could 
1 - Make captured data members public and accessible by their capture names.
2 - Make the "param decl ()" and "body {}"  parts of lambda optional.
3 - When item 2 happens, set the default to "not const/mutable" members, and also allow "const" somewhere before or after the [] to override 'not const' default; 

The 'person' example would be like this:
auto person = [name = "Bill"s, age = 25]; // <- allow () and [] be optional in lambda definition.
int m = person.age;

Other examples:
auto l = [x, &y, z = u + " expression" ]; 
int k = l.x + l.y + l.z.size() + l.w;

auto person2 =       [name = "Bill"s; age = short(25)]; // forcing the string and short types
auto person3 = const [name = "Bill"s; age = short(25)]; // making const members
const auto person3 = [name = "Bill"s; age = short(25)]; // members are not const, the variable 'person3' is.

template<typename T>
void foo(const T&...p) {
   cout << p.a << " " << p.b << "  " << p.c;
}

foo( [a=1, b=2, c=3] );  // Yeah ! you can call a function too !
foo( [a="rhs structured bindings ?", b="or heterogeneous initialization list ?" , c="or named parameters?" ] );

4 - On top of that, if we allow a scope after [] and define any members:

auto lambda = [x, y] {
  int z = 8;
  int square(){ return z*z; };
};

lambda.x = lambda.
square() + lambda.y;

and the lambda will be expanded as usual, even the constructor for capture ( witch is one of main reason of this ), but with two new members (z=8 and foo) in the definition.


5 - Don´t ask about "this", some duck-taping was made in "normal" lambdas, I´m not sure but I think we cannot apply the same idea here.
6 - Neither about non-default constructors (since we do not know the name of the lambda type) if you want that, please use "normal" structures.



BR
Cleiton
Reply all
Reply to author
Forward
0 new messages