Why is the name of the files affecting the structs in C++?

Using the below code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//h.cpp
#include "main.h"
#include "api/core.hpp"
extern pros::Motor frontLeft;
extern pros::Motor rearLeft;
extern pros::Motor frontRight;
extern pros::Motor rearRight;
extern pros::Motor leftLift;
extern pros::Motor rightLift;
extern pros::Motor leftRoller;
extern pros::Motor rightRoller;
Motor_Group fullDrive({frontLeft,rearLeft,frontRight,rearRight});
Motor_Group leftDrive({frontLeft,rearLeft});
Motor_Group rightDrive({frontRight,rearRight});
Motor_Group lift({leftLift,rightLift});
Motor_Group roller({leftRoller,rightRoller});
Motor_Group test({frontLeft,rearLeft});

1
2
3
4
5
6
7
8
9
10
//config.h
#include "api/core.hpp"


extern Motor_Group test;
extern Motor_Group fullDrive;
extern Motor_Group leftDrive;
extern Motor_Group rightDrive;
extern Motor_Group lift;
extern Motor_Group roller;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//initialize.cpp
#include "main.h"
#include "config.h"
pros::Motor frontLeft(1);
pros::Motor rearLeft(2);
pros::Motor frontRight(3);
pros::Motor rearRight(4);
pros::Motor leftLift(1);
pros::Motor rightLift(1);
pros::Motor leftRoller(1);
pros::Motor rightRoller(1);
void initialize(){
  pros::lcd::initialize();
  test.move(127);
}

the `.move()` function does not work as expected. The motors does not move a bit. I believe that the `extern test` creates an empty class. I think this is because of the alphabetical order the compiler compiles things. If I rename h.cpp to j.cpp(which is alphabetically after initialize), it works perfectly fine. Is there a way to work around this or do the h.cpp file should be alphabetically after all other cpp files? I tried changing the name of the config.h file but it doesn't matter.
Edit:For you info, here is core.h and core.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//core.h
#ifndef _CORE_HPP_
#define _CORE_HPP_
#include "main.h"
#include <vector>
#include <map>
#include <functional>
#include <initializer_list>

void wait(int ms);
struct Motor_Group{
  std::vector<pros::Motor> motor_vector;

  int speed = 0;

  Motor_Group(std::initializer_list<pros::Motor> port_set);

  pros::Motor get(short idx);

  short size();

  void move(short speed);

  float position();

  void tare();
};
/*...*/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//core.cpp
#include "globals.h"
#include "main.h"
#include "core.hpp"
#include <functional>
#include <map>
void wait(int ms){
  pros::delay(ms);
}
Motor_Group::Motor_Group(std::initializer_list<pros::Motor> port_set)
{
  this->motor_vector = std::vector(port_set);
}
pros::Motor Motor_Group::get(short idx)
{
  return this->motor_vector[idx];
}
short Motor_Group::size()
{
  return this->motor_vector.size();
}
void Motor_Group::move(short speed)
{
  for(int i = 0;i<motor_vector.size();i++)
  {
    this->motor_vector[i].move(speed);//<- this move function is a function from the api in the 'main.h' , not the Motor_Group.move() one
    pros::lcd::print(i+1,"moved");
  }
  this->speed = speed;
}
float Motor_Group::position()
{
  float total = 0;
  for(int i = 0;i<size();i++)
  {
    total += this->motor_vector[i].get_position();
  }
  return total / this->size();
}
void Motor_Group::tare()
{
  for(int i = 0;i<this->size();i++)
  {
    this->motor_vector[i].tare_position();
  }
}

/*...*/
The compiler does not actually compile files alphabetically.
You are probably using *.cpp on the command line, which provides an alphabetical list of cpp files and the compiler is compiling them in the given order.
But the order of compilation should not matter in general, so I'm not sure what's going on with your code.
Last edited on
Is this the correct way of "externing" a class object?
Yes, I think the externs are okay.

It could be "undefined behavior".
It's probably the linking order that's changing things by creating a somewhat different memory layout in the two cases.
If that's true then there is an underlying error in your code, such as accessing a vector outside it's bounds.
Last edited on
I tried to create a test program which only have these files, and the thing still happened
Im so confused!!!
I don't know what to say. :-(

Help us Obi-Wan Niccollo (or Darth Borges) you're our only hope!
LOL :| i really have no hope. I was trying to debug this for a week and found that... The names of the files matters???
Can you provide a link to a zip file of all the code?

Since you are creating global objects in h.cpp and initialize.cpp, the storage for these (except for the dynamic part of the vector storage) will go in a static data area. Apparently, the order in which those global objects are put into that area is affecting your program. That means that you are probably overflowing one of the objects and writing into the next one at some point. You need to look for an error of that type.\

Maybe add a check to your get routine to see if idx is ever out of range (below 0 or equal to or above the vector size).
Last edited on
I can, i will send it later. The thing now is that changing the name will solve it. In my main project with more files, this works but some files which included the h file still resulted in the same thing
> If I rename h.cpp to j.cpp(which is alphabetically after initialize), it works perfectly fine.
> Is there a way to work around this

This appears to be a classic example of 'static initialization order fiasco'
https://isocpp.org/wiki/faq/ctors#static-init-order

One simple solution: https://isocpp.org/wiki/faq/ctors#static-init-order-on-first-use

A more elaborate technique: http://www.petebecker.com/js/js199905.html
Note that in the example at the end of Becker's article, the array of characters must be properly aligned:
1
2
// static char out_data[sizeof(std::ofstream)];
namespace { alignas(std::ofstream) char out_data[sizeof(std::ofstream)]; }
Last edited on
@HEAden,

The main point given to you thus far is this:

What you describe should not happen, and those of us with many years or decades of experience recognize the problem in the sense that nonsensical "solutions" are actually a symptom of the problem(s) described to you.

This means that while it may seem that renaming solves the problem, we can assure you it hasn't. Minor alterations in the layout of the executable will undoubtedly bring the problem forward again in the future. It is merely hidden, not solved.

@dutch - you crack me up! That was just wonderful!


Had anyone has experience this? Is this normal to happen to global class objects? I have done the same thing using "extern" when using global variables. Why is this only happening with class objects? Is this normal?
@JLBorges the thing is that it doesnt corupt 50% the time, it doesnt work 100% of teh time
"50% of the time" just means that you have a 50/50 chance of it working, depending on how the compiler orders the construction code. In your case, changing the order of the files listed on the compile line is what "flips the coin".

You need to ensure that the Motor objects are created before the Motor_Group objects that copy them, otherwise the Motor_Group objects will get uninitialized Motor objects, which is basically what you are seeing.

I think that putting all the globals in a single cpp file with the Motor objects before the Motor_Group objects would solve it, if that is feasible in your situation.

Another possibility is to store pointers/references to the Motor objects in the Motor_Groups instead of copying them.

EDIT: Here's a program that demonstrates the problem:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// one.cpp //////////////////////////////////////////

#include <iostream>
#include "object.h"

extern Object a;

Object b(a);

int main()
{
    std::cout << b.get() << '\n';
}


// two.cpp //////////////////////////////////////////

#include "object.h"

Object a;


// object.h //////////////////////////////////////////
#pragma once

class Object
{
    int n;
public:
    Object() : n(42) {}
    int get() const { return n; }
};


For me, compiling as g++ -std=c++11 -Wall -W -pedantic two.cpp one.cpp prints 42
but compiling as g++ -std=c++11 -Wall -W -pedantic one.cpp two.cpp prints 0.
Last edited on
> Had anyone has experience this? Is this normal to happen to global class objects?

Yes.
Even the standard library has to address this problem. See https://en.cppreference.com/w/cpp/io/ios_base/Init


> Why is this only happening with class objects?

It can happen with any object that requires dynamic initialisation.
See Unordered dynamic initialization https://en.cppreference.com/w/cpp/language/initialization#Dynamic_initialization

For example:
a.cpp
1
2
3
#include <random>

unsigned long long g = std::random_device{}() ;


b.cpp
1
2
extern unsigned long long g ;
unsigned long long g2 = g ; // g2 may be initialised before g (and thus get a value of zero) 
Here is the zip:https://drive.google.com/open?id=1_6Gsd87t8CAk_woXb-cmkHGs1u48PzaF
Edit: the file "main.h" is not available but its just the part which has the pros::Motor type
Last edited on
Here is the solution:
1
2
3
4
5
Motor_Group& leftDrive()
{
  static Motor_Group* leftDrive = new Motor_Group({frontLeft,rearLeft});
  return *leftDrive;
}
Topic archived. No new replies allowed.