std::array of objects in initializer list

OP #4793710
Lesenswert?
• ▲
▼
Kann man die Position eines Objektes, welches in einem std::array liegt 
seinem eigenen Konstruktor übergeben?

folgendes Problem:
1
//Actor.h
2
class Actor {
3
public:
4
    Actor(const std::string &moduleName, int baseAddress);
5
}
6

7
//Device.h
8
class Device{
9
public:
10
    Device();
11

12
private:
13
    std::array<Actor, 16> actorDigital;
14
}
15

16
//Device.cpp
17
Device::Device() :
18
  // std::array needs double {{..}} to initialize objects
19
  actorDigital{{   Actor("DigitalOut1"),
20
    Actor("DigitalOut2"),
21
    Actor("DigitalOut3"),
22
    Actor("DigitalOut4"),
23
    Actor("DigitalOut5"),
24
    Actor("DigitalOut6"),
25
    Actor("DigitalOut7"),
26
    Actor("DigitalOut8"),
27
    Actor("DigitalOut9"),
28
    Actor("DigitalOut10"),
29
    Actor("DigitalOut11"),
30
    Actor("DigitalOut12"),
31
    Actor("DigitalOut13"),
32
    Actor("DigitalOut14"),
33
    Actor("DigitalOut15"),
34
    Actor("DigitalOut16")
35
  }}
36
{
37
}

Zuerst hatte ich noch keine baseAddress im Actor() und habe von Hand die 
Namen vergeben, was mir eigentlich schon nicht gefiel. Nun müsste ich 
noch anhand des Index die baseAddress vergeben, dies wäre etwas in der 
Art:

address + (index * x)

Genauso könnte ich den Namen generieren lassen, ohne von Hand 
durchzuzählen, mein Ziel wäre:

Actor("DigitalOut" + to_string(index), address + (index * x) )

Gibt es einen Weg das std::array so zu initialisieren?
OP #4793736
Lesenswert?
• ▲
▼
Kannst du mir mehr über die statischen Datenmember sagen? (Link zu 
beispiel oder so)

Würde ich stattdessen das array mit pointer füllen, hätte ich vermutlich 
so etwas gemacht:
1
for(unsigned int i=0; i<actorDigital.size(); ++i){
2
    actorDigital[i] = new Actor(("DigitalOut" + to_string(i), 0x100 + (i * 0x04) );
3
}

Bei dieser Lösung könnte ich aber gleich zum vector<> gehen und hätte 
das ganze sauberer gelöst.

(Das es hier im Beispiel bei 0 Anfangen würde und nicht bei 1 soll mal 
ausser acht gelassen werden, aber ich denke die Idee ist klar.)
#4793739
Lesenswert?
• ▲
▼
Am einfachsten in solchen Fällen ist es doch, das einfach mal 
auszuprobieren (und schön wäre es, wenn dann im Ausgangsbeitrag schon 
compilierbarerer Code gepostet wird):
1
#include <array>
2
#include <iostream>
3

4
class Actor
5
{
6
public:
7
  const std::string Name;
8
  const int index;
9
    Actor(const std::string &moduleName, int baseAddress) : Name(moduleName),
10
                                index(baseAddress)
11
{}
12
};
13

14
class Device{
15
public:
16
    Device();
17

18
public:
19
    int index = 0;
20
    const int address = 1;
21
    std::array<Actor, 2> actorDigital;
22
};
23

24
//Device.cpp
25
Device::Device() :
26
  // std::array needs double {{..}} to initialize objects
27
  actorDigital{   Actor("DigitalOut" + std::to_string(index), index++),
28
                Actor("DigitalOut" + std::to_string(index), index++)
29
              }
30
{
31
}
32

33
int main()
34
{
35
  Device d;
36
  for (const auto& a : d.actorDigital)
37
  {
38
    std::cout << a.Name << " " << a.index << std::endl;
39
  }
40
}

DigitalOut1 0
DigitalOut2 1


Oliver
Gast #4793741
Lesenswert?
• ▲
▼
Oliver S. schrieb:
> Am einfachsten in solchen Fällen ist es doch, das einfach mal
> auszuprobieren:

Das ist aber undefiniertes Verhalten , pures Glück dass es funktioniert. 
Wenn man eine Variable (hier Index) innerhalb eines Ausdrucks mehrfach 
verändert passiert etwas Compiler spezifisches...
#4793742
Lesenswert?
• ▲
▼
Dachte etwa an:
1
#include <array>
2
#include <string>
3
#include <iostream>
4

5
class Actor
6
{
7
public:
8
    Actor(const std::string& baseName = "bla") : name(baseName + std::to_string(numberOfActors++)) {}
9
//private:
10
    static size_t numberOfActors;
11
    std::string name;
12
};
13
size_t Actor::numberOfActors = 0;
14

15
std::ostream& operator<<(std::ostream& o, const Actor& a) {
16
    return o << a.name;
17
}
18

19
int main()
20
{
21
    std::array<Actor, 3> actors;
22
    
23
    for(auto& a: actors) {
24
        std::cout << a << std::endl;
25
    }
26
        
27
}
#4793751
Lesenswert?
• ▲
▼
Oder so:
1
class Actor
2
{
3
public:
4
    friend std::ostream& operator<<(std::ostream& o, const Actor& a);
5
    Actor(const std::string& baseName = "bla") : name(baseName + std::to_string(numberOfActors)), 
6
        number(numberOfActors++) {}
7
private:
8
    static size_t numberOfActors;
9
    std::string name;
10
    const size_t number;
11
};
12
size_t Actor::numberOfActors = 0;
13

14
std::ostream& operator<<(std::ostream& o, const Actor& a) {
15
    return o << a.name;
16
}
17

18
int main()
19
{
20
    std::array<Actor, 3> actors;
21
    
22
    for(const auto& a: actors) {
23
        std::cout << a << std::endl;
24
    }
25
}
OP #4793774
Lesenswert?
• ▲
▼
Danke für Antworten, es scheint also keine direkte Lösung über 
std::array zu geben?

Beitrag "Re: std::array of objects in initializer list"

An dieser Lösung fehlt mir der boundary check, der ja mit array möglich 
wäre. Ebenso muss wieder viel geschrieben werden, wollte das eigentlich 
vermeiden und nur eine "vorlage" schreiben, die der Compiler richtig 
generiert.

Beitrag "Re: std::array of objects in initializer list"

Dieser Ansatz habe ich auch schon angedacht, aber wenn ich ein zweites 
array ala

std::array<Actor, 3> actorsDigital;
std::array<Actor, 3> actorsAnalog;

habe, funktioniert es wieder nicht. Der index müsste auf das array 
bezogen sein und nicht auf die Objekte.

std::array wollte ich einsetzen da ich eine feste Grösse habe, aber 
nicht auf Actor actor[16] setzen wollte, alleine schon wegen 
range-based-loops. Irgendwie bin ich aber nie zufrieden mit dem 
std::array, so dass ich doch raw-arrays oder std::vector einsetze - 
schade.


Bezüglich dem compilierbaren code: Wie so oft ist die eigentliche Klasse 
viel grösser und einige Namen mussten firmenbezogen obfuscated werden. 
Werde mir aber in Zukunft mehr mühe geben.
(Firma: Torrox.de) #4793783
Lesenswert?
• ▲
▼
Operator S. schrieb:
> Actor("DigitalOut" + to_string(index), address + (index * x) )
>
> Gibt es einen Weg das std::array so zu initialisieren?

Fast! ;-) Es wäre "nur" ein Array und die Lösung ist etwas unleserlich:
1
#include <tuple>
2
#include <type_traits>
3
#include <iostream>
4
#include <array>
5

6
class actor
7
{
8
public:
9
    explicit actor( int i ) : id_( "DigitalOut" + std::to_string( i ) ) {}
10

11
    std::string id() const
12
    {
13
        return id_;
14
    }
15

16
private:
17
    const std::string id_;
18
};
19

20
template < typename T >
21
struct generate_actor;
22

23
template < int I >
24
struct generate_actor< std::integral_constant< int, I > >
25
{
26
    static const actor a;
27
};
28

29
template < int I >
30
const actor generate_actor< std::integral_constant< int, I > >::a = actor( I );
31

32
template < typename ...Ts >
33
struct unroll_actors;
34

35
template < typename ...Ts >
36
struct unroll_actors< std::tuple< Ts... > >
37
{
38
    static const actor actors[ sizeof ...(Ts) ];
39
};
40

41
template < typename ...Ts >
42
const actor unroll_actors< std::tuple< Ts... > >::actors[ sizeof ...(Ts) ] = { generate_actor< Ts >::a... };
43

44
template < typename A, typename B >
45
struct add;
46

47
template < typename ...As, typename ...Bs >
48
struct add< std::tuple< As... >, std::tuple< Bs... > >
49
{
50
    using type = std::tuple< As..., Bs... >;
51
};
52

53
template < int I >
54
struct generate_actor_args
55
{
56
    using type = typename add<
57
        typename generate_actor_args< I - 1 >::type,
58
        std::tuple< std::integral_constant< int, I > >
59
    >::type;
60
};
61

62
template <>
63
struct generate_actor_args< 1 >
64
{
65
    using type = std::tuple< std::integral_constant< int, 1 > >;
66
};
67

68
int main()
69
{
70
    const actor (&theater)[ 16 ] = unroll_actors< generate_actor_args< 16 >::type >::actors;
71

72
    for ( const auto a : unroll_actors< generate_actor_args< 16 >::type >::actors )
73
        std::cout << a.id() << std::endl;
74
}
#4793804
Lesenswert?
• ▲
▼
So villt:
1
struct Digital {
2
    static constexpr const char name[] = "D";
3
};
4
struct Analog {
5
    static constexpr const char name[] = "A";
6
};
7

8
template<typename Type>
9
class Actor
10
{
11
public:
12
    Actor(const std::string& baseName = Type::name) : name(baseName + std::to_string(numberOfActors)), 
13
        number(numberOfActors++) {}
14
    static size_t numberOfActors;
15
    std::string name;
16
    const size_t number;
17
};
18
template<typename Type>
19
size_t Actor<Type>::numberOfActors = 0;
20

21
template<typename Type>
22
std::ostream& operator<<(std::ostream& o, const Actor<Type>& a) {
23
    return o << a.name;
24
}
25

26
int main()
27
{
28
    std::array<Actor<Digital>, 3> actors1;
29
    std::array<Actor<Analog>, 3> actors2;
30
    
31
    for(const auto& a: actors1) {
32
        std::cout << a << std::endl;
33
    }
34
    for(const auto& a: actors2) {
35
        std::cout << a << std::endl;
36
    }
37
}
#4793828
Lesenswert?
• ▲
▼
Dr. Sommer schrieb:
> Das ist aber undefiniertes Verhalten , pures Glück dass es funktioniert.
> Wenn man eine Variable (hier Index) innerhalb eines Ausdrucks mehrfach
> verändert passiert etwas Compiler spezifisches...

Hm. Ich erkenne deine Kompetenz normalerweise neidlos an, aber...

... hätte da gerne etwas mehr Info.

Der Standard (C++11, Draft N3376) schreibt:
1
Within the initializer-list of a braced-init-list, the initializer-clauses, 
2
including any that result from pack expansions (14.5.3), are evaluated in the order in which they appear. That is, every value computation and
3
side effect associated with a given initializer-clause
4
is sequenced before every value computation and side effect associated with any initializer-clause that follows it in the comma-separated list of the
5
initializer-list.

Oliver
#4793874
Lesenswert?
• ▲
▼
Nochwas:
1
template<typename Type>
2
class Actor2
3
{
4
public:
5
    Actor2(int id) : name(std::string{Type::name} + std::to_string(id)), 
6
        mId(id) {}
7
    std::string name;
8
    const size_t mId;
9
};
10

11
template<typename Type>
12
std::ostream& operator<<(std::ostream& o, const Actor2<Type>& a) {
13
    return o << a.name;
14
}
15

16
Actor2<Digital> operator"" _da(unsigned long long v) {
17
    return Actor2<Digital>(v);
18
}
19

20
Actor2<Analog> operator"" _aa(unsigned long long v) {
21
    return Actor2<Analog>(v);
22
}
23

24
int main()
25
{
26
    std::array<Actor2<Digital>, 3> actors3 = {{1_da, 3_da, 2_da}};
27
    std::array<Actor2<Analog>, 3> actors4 = {{1_aa, 3_aa, 2_aa}};
28
}
#4793995
Lesenswert?
• ▲
▼
Operator S. schrieb:
> Kann man die Position eines Objektes, welches in einem std::array liegt
> seinem eigenen Konstruktor übergeben?

Ich hatte mir mal ein make_array geschrieben, um std::array für 
bestimmte Fälle "dynamisch" initialisierbar zu machen (nicht exception 
safe). Dann könnte man in deinem Fall bspw. über einen std::vector gehen 
und folgerndermaßen verfahren:
1
#include <array>
2
#include <iostream>
3
#include <sstream>
4
#include <string>
5
#include <vector>
6

7
template <size_t N,typename Iterator>
8
static std::array<typename std::iterator_traits<Iterator>::value_type,N> make_array(Iterator first,Iterator last)
9
{
10
  using T = typename std::iterator_traits<Iterator>::value_type ;
11
  char buffer[sizeof(std::array<T,N>)] ;
12
  auto *p = reinterpret_cast<std::array<T,N>*>(&buffer) ;
13
  for (size_t i=0 ; i<N ; ++i) {
14
    if (first == last)  
15
      throw std::runtime_error("make_array:underflow") ;
16
    new (&(*p)[i]) T(*first) ;
17
    ++first ;
18
  }
19
  if (first != last) 
20
    throw std::runtime_error("make_array:overflow") ;
21
  auto array = (*p) ;
22
  for (size_t i=0 ; i<N ; ++i) {
23
    (*p)[i].~T() ;
24
  }
25
  return array ;
26
}
27

28
struct Actor
29
{
30
  std::string s ; int i ;
31
  Actor(std::string const &s, int i) : s(s),i(i) {}
32
} ;
33

34
struct Device
35
{
36
  static Device make()
37
  {
38
    std::vector<Actor> v ;
39
    for (int i=1 ; i<=16 ; ++i)
40
      v.push_back(Actor("DigitalOut"+std::to_string(i),i)) ;
41
    return Device(v) ;
42
  }
43
  
44
  Device(std::vector<Actor> const &v) : a(make_array<16>(v.begin(),v.end())) {}
45
  
46
  std::string to_str() const
47
  {
48
    std::ostringstream os ;
49
    for (auto &actor : a)
50
      os << actor.i << ':' << actor.s << '\n' ;
51
    return os.str() ;
52
  }
53
      
54
private:
55
  std::array<Actor,16> a ;
56
} ;
57

58
int main()
59
{
60
  auto d = Device::make() ;
61
  std::cout << d.to_str() ;
62
  return 0 ;
63
}
#4794105
Lesenswert?
• ▲
▼
Also ich finds so ganz hübsch:
1
#include <array>
2
#include <string>
3
#include <iostream>
4

5
struct Digital {
6
    static constexpr const char name[] = "Digital";
7
    static constexpr const uint16_t baseAddress = 0x100;
8
};
9
struct Analog {
10
    static constexpr const char name[] = "Aanalog";
11
    static constexpr const uint16_t baseAddress = 0x200;
12
};
13

14
template<typename Type> class Actor;
15
template<typename Type> std::ostream& operator<<(std::ostream& o, const Actor<Type>& a);
16

17
template<typename Type>
18
class Actor
19
{
20
    friend std::ostream& operator<< <>(std::ostream& o, const Actor<Type>& a);
21
public:
22
    Actor(int id) : name(std::string{Type::name} + std::to_string(id)), mId(id), address(Type::baseAddress + mId) {}
23
private:
24
    std::string name;
25
    const size_t mId;
26
    const uint16_t address;
27
};
28

29
template<typename Type>
30
std::ostream& operator<<(std::ostream& o, const Actor<Type>& a) {
31
    return o << a.name << " : " << a.address;
32
}
33

34
Actor<Digital> operator"" _da(unsigned long long v) {
35
    return Actor<Digital>(v);
36
}
37

38
Actor<Analog> operator"" _aa(unsigned long long v) {
39
    return Actor<Analog>(v);
40
}
41

42
int main()
43
{
44
    std::array<Actor<Digital>, 3> actors1 = {{1_da, 3_da, 2_da}};
45
    std::array<Actor<Analog>, 3>  actors2 = {{1_aa, 3_aa, 2_aa}};
46
    
47
    for(const auto& a: actors1) {
48
        std::cout << a << std::endl;
49
    }
50
    for(const auto& a: actors2) {
51
        std::cout << a << std::endl;
52
    }
53
}

vielleicht triffts ja den Geschmack...
OP #4794505
Lesenswert?
• ▲
▼
Danke für all die Lösungen. Ich hatte gehofft irgendwie std::fill oder 
std::generate einsetzen zu können, um näher am standard zu bleiben und 
weniger eigenen Code zu produzieren, aber das scheint in diesem Fall 
nicht möglich zu sein.
Auch sonst im Netz scheints immer auf template magic hinauszulaufen :)

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren