Can Templates Be Used to Access Struct Variables by Name

Can templates be used to access struct variables by name?

#include <iostream>
#include <ostream>
#include <string>

struct my_struct
{
int a;
std::string b;
};

template <typename TObject, typename TMember, typename TValue>
void set( TObject* object, TMember member, TValue value )
{
( *object ).*member = value;
}

class undo_token {};

template <class TValue>
class undo_member : public undo_token
{
TValue new_value_;
typedef TValue my_struct::* TMember;
TMember member_;

public:
undo_member(TMember member, TValue new_value):
new_value_( new_value ),
member_( member )
{}

void undo(my_struct *s)
{
set( s, member_, new_value_ );
}
};

int main()
{
my_struct s;

set( &s, &my_struct::a, 2 );
set( &s, &my_struct::b, "hello" );

std::cout << "s.a = " << s.a << std::endl;
std::cout << "s.b = " << s.b << std::endl;

undo_member<int> um1( &my_struct::a, 4 );
um1.undo( &s );

std::cout << "s.a = " << s.a << std::endl;

undo_member<std::string> um2( &my_struct::b, "goodbye" );
um2.undo( &s );

std::cout << "s.b = " << s.b << std::endl;

return 0;
}

Template function to access struct members

An easy way to achieve that is to use function pointers as parameters to your template function:

// Example program
#include <iostream>
#include <string>

struct B{
int y;
int z;
};

struct A{
int x;
B b;
};

int& get1( A& a ) { return a.x; }
int& get2( A& a ) { return a.b.y; }

template <typename T>
bool doSomething(A *a, T& (*getter)( A& ) ){

T& attr = (*getter)( *a ); // get reference to the attribute
attr = 5; // modify the attribute
return true;
}

int main()
{
A myA;

doSomething(&myA, &get1);
doSomething(&myA, &get2);
}

Access struct member by name in Go template

Create a template function that returns the struct field names and values as a map:

// fields returns map of field names and values for struct s.
func fields(s interface{}) (map[string]interface{}, error) {
v := reflect.Indirect(reflect.ValueOf(s))
if v.Kind() != reflect.Struct {
return nil, fmt.Errorf("%T is not a struct", s)
}
m := make(map[string]interface{})
t := v.Type()
for i := 0; i < t.NumField(); i++ {
sv := t.Field(i)
m[sv.Name] = v.Field(i).Interface()
}
return m, nil
}

Specify the function when parsing the file:

t, err := template.New("").Funcs(template.FuncMap{"fields": fields}).ParseFiles("table.gohtml")
if err != nil {
panic(err)
}

Use it like this:

{{range $i, $v :=  .Model}}
<tr>
{{$m := fields $v}}
{{range $key, $value := $fields}}
<td>{{index $m $value}}</td>
{{end}}
</tr>
{{end}}

Run it on the playground.

Another approach is to write a function that looks up a field by name:

func field(s interface{}, k string) (interface{}, error) {
v := reflect.Indirect(reflect.ValueOf(s))
if v.Kind() != reflect.Struct {
return nil, fmt.Errorf("%T is not a struct", s)
}
v = v.FieldByName(k)
if !v.IsValid() {
return nil, fmt.Errorf("no field in %T with name %s", s, k)
}
return v.Interface(), nil
}

Parse with the function:

t, err := template.New("").Funcs(template.FuncMap{"field": field}).ParseFiles("table.gohtml")

Use it like this:

{{range $i, $v :=  .Model}}
<tr>
{{range $key, $value := $fields}}
<td>{{field $v $value}}</td>
{{end}}
</tr>
{{end}}

Run it on the playground.

How can I use different struct as template argument in a template function?

What else I can do to slove this problom in one single function?

This is not possible under c++11 compiler. However, in C++17 you have if constexpr to do such:

template<typename T>
std::string EncodeData(int DataType, T const& Data, std::string const& ReadCommandID, std::string& ThisID)
{
if constexpr (std::is_same_v<T, A>)
{
// Do something with Data.A_data and Data.A_data_2
}
else if constexpr (std::is_same_v<T, B>)
{
// Do something with Data.B_data and Data.B_data_2
}
}

For C++11 you still need two functions. Therefore, I would suggest having a function overload for each, which might be more readable than having templates.

Struct with template variables in C++

The problem is you can't template a typedef, also there is no need to typedef structs in C++.

The following will do what you need

template <typename T> 
struct array {
size_t x;
T *ary;
};

Template struct in C++ with different data members

Yes, you can do it with partial specialization.

template<int Asize, typename = void> struct intxA
{
};
template <int Asize>
struct intxA<Asize, std::enable_if_t<Asize <= 8>>
{
int8 num = 0;
};
template <int Asize>
struct intxA<Asize, std::enable_if_t<(Asize > 8 && Asize <= 16)>>
{
int16 x = 1;
};

Variable with same name with a struct type compiling only if it's not a template

According to the C++ Standard (C++ 20, 13 Templates) class template name shall be unique in its declarative region.

7 A class template shall not have the same name as any other template,
class, function, variable, enumeration, enumerator, namespace, or type
in the same scope (6.4), except as specified in 13.7.6. Except that a
function template can be overloaded either by non-template functions
(9.3.4.6) with the same name or by other function templates with the
same name (13.10.4), a template name declared in namespace scope or in
class scope shall be unique in that scope.

So for example this declaration in main

template<bool x = true>
struct Foo {
int func(int) const;
};

int main()
{
struct Foo<> Foo;
}

will be correct.

As for these declarations

struct Foo {
int func(int) const;
};

struct Foo Foo;

then the declaration of the variable Foo hides the name of the declared structure. In C structure tag names and variable names are in different name spaces. So to preserve the compatibility with C such declarations in C++ are allowed.

To refer to the structure type after the variable declaration you need to use the elaborated name of the structure type.

Can I access a struct/class member from a template?

It's perfectly legal, templates in C++ are not comparable to a different approach (think Java) that type checks the generic method or classes by keeping the type variable.

A C++ template is compiled with every possible type you are using it, so every single instantiation for every specific type is compiled and type checked. If you try to access a field which is not contained in the type you are using doSomething with, then you will get a compiler error.



Related Topics



Leave a reply



Submit