TCanvas as a private variable of class

Hello, I am having a problem when trying to use a TCanvas as a private variable in a class, and using it in a method of that class! Here are the files in which there is a problem:

File Functor.h

class Functor
{
 public:

  // Constructor/Destructor
  Functor(string s="Functor");
  ~Functor() = default;

  // Methods
  virtual double operator()(double x);
  void Draw(double xi, double xf, int num, string xtitle, string ytitle);
  
 protected:
  TCanvas *c;  // Here is the variable of the TCanvas
  string name;
};

File Functor.cpp

Functor::Functor(string s) :name(s)
{
  c = new TCanvas("c","c",900,800);
}
double Functor::operator()(double x)
{
  return 0;
}

void Functor::Draw(double xi, double xf, int num, string  xtitle, string ytitle)
{
  double x[num], y[num], h = (xf-xi)/((double)num);

  // Values for TGraph
  for(int i=0; i<num ; i++)
    {
      x[i] = xi + i*h;
      y[i] = this->operator()(x[i]);
    }

  TApplication app("app",nullptr,nullptr);
  TGraph* graph = new TGraph(num, x, y);

  // Aesthetics 
  graph -> SetTitle(name.c_str());
  graph -> GetXaxis() -> SetTitle(xtitle.c_str());
  graph -> GetYaxis() -> SetTitle(ytitle.c_str());
  graph -> GetXaxis() -> CenterTitle(true);
  graph -> GetYaxis() -> CenterTitle(true);
  graph -> SetLineColor(kRed);
  graph -> SetMarkerColor(kRed);
  
  graph->Draw("APL");

  // Legend
  auto legend = new TLegend(0.1,0.7,0.48,0.9);
  legend->SetHeader("Legends","C"); // option "C" allows to center the header
  legend->AddEntry("graph","Funtion","l");
  legend->Draw();

  //Canvas Interface
  c->Modified();
  c->Update();
  TRootCanvas *rc = (TRootCanvas *)c->GetCanvasImp();
  rc->Connect("CloseWindow()","TApplication",gApplication,"Terminate()"); // The error occurs here
  app.Run();
}

(The operator overloading is supposed to be overridden so don`t worry)

The error is this:

*** Break *** segmentation violation

===========================================================
There was a crash.
...
===========================================================
#6  0x00007efeeb1d33c8 in TQObject::Connect(char const*, char const*, void*, char const*) () from /usr/local/root_v6_24/lib/libCore.so.6.24
#7  0x000055a2a29203ba in Functor::Draw (this=0x7ffffb61bc20, xi=-5, xf=5, num=1000, xtitle="x", ytitle="y") at src/Functor.cpp:50
===========================================================

Thanks for your help!

_ROOT Version: 6.24/06
_Platform: Ubuntu 20.04
_Compiler: GCC


Hello,

@couet can you give a hand here? Thanks!

Please note that delays in answering might be bigger during the Christmas break.

I am not sure to understand how your are using your code. In particular I do not see where you include the .h file in the .cpp one. Can you provide a small example using your code ?

Here is a simplification of my code that exhibits the same problem:
File Functor.h

#ifndef __FUNCTOR__
#define __FUNCTOR__

#include <iostream>
#include <string>
#include "TCanvas.h"
#include "TF1.h"
#include "TApplication.h"
#include "TGraph.h"
#include "TROOT.h"
#include "TPad.h"
#include "TRint.h"
#include "TAxis.h"
#include "TCanvas.h"
#include "TRootCanvas.h"
#include "TLatex.h"
#include "TLegend.h"

class Functor
{
 public:

  // Constructor/Destructor
  Functor(std::string s="Functor");
  ~Functor() = default;

  // Methods
  void Draw();

  // Getters
  TCanvas *GetCanvas();
  
 protected:
  TCanvas *c;
  std::string name;
};

#endif

File Functor.cpp

#include "Functor.h"

using namespace std;

Functor::Functor(string s) :name(s)
{
  c = new TCanvas("c","c",900,800);
}

void Functor::Draw()
{
  double x[10], y[10];

  // Values of TGraph
  for(int i=0; i<10 ; i++)
    {
      x[i] = i;
      y[i] = 2*i + 1;
    }

  TApplication app("app",nullptr,nullptr);
  TGraph* graph = new TGraph(10, x, y);

  // Aesthetics
  graph -> SetTitle("MyGraph");
  graph -> GetXaxis() -> SetTitle("x");
  graph -> GetYaxis() -> SetTitle("y");
  graph -> SetLineColor(2);
  graph -> SetLineWidth(2);
  graph -> SetMarkerColor(2);
  graph -> SetName("graph");
  
  graph->Draw("APL");

  // Legenda
  auto legend = new TLegend(0.1002227,0.8195876,0.2639198,0.9007732);
  legend->SetHeader("Legends","C");
  legend->AddEntry("graph","Funtion","l");
  legend->Draw();

  //Interfaces do canvas
  c->Modified();
  c->Update();
  TRootCanvas *rc = (TRootCanvas *)c->GetCanvasImp();
  rc->Connect("CloseWindow()","TApplication",gApplication,"Terminate()");
  app.Run();
}

TCanvas *Functor::GetCanvas()
{
  return c;
}

Example of file main.cpp (driver code)

#include "Functor.h"

int main()
{
  Functor F("MyFunctor");

  F.Draw();
}

The fundamental problem is that I am trying to use a TCanvas variable which the Draw method will use in order to display the graphics! I am doing this because I want to be able to extract this TCanvas variable (Using the GetCanvas method) in oder to use it in other methods of other classes that can add “Stuff” to the canvas before calling the Draw method of the Functor class. I dont know if I can do this or if there is a better way of doing it but I would appreciate if someone knew another way! The error that I am getting is the one that I pasted in the original question. It is a memory error I guess. What puzzles me is, how does the Draw method know that I want to put that "stuff"(TGraph, TLegend ....) in the c TCanvas variable if I dont make any reference to it until almost the end of the Draw method (I guess the problem has to do with that): Is there a TCanvas method that says like “This is the current canvas! Put the next stuff in this canvas.” (I suppose this goes directly into the workings of the Draw methods of TGraph and other Root classes?)
Thank you, I hope my problem is more understandable now, and sorry for my first confusing question!

Thanks for the details.
Which command are you using to compile and link your program ?

I am using the following makefile:

#### Variable definitions

CXX := g++
CXXFLAGS := $(shell root-config --cflags) -fPIC -Wall -g

ROOT_INC := -I $(shell root-config --incdir)
ROOT_LIB := $(shell root-config --libs)
EIGEN_INC := -I /usr/include/eigen3
LIBS := -L lib -lFC $(ROOT_LIB)
INCS := -I src $(EIGEN_INC) $(ROOT_INC)

SRCS := $(wildcard src/*.cpp)
OBJS := $(patsubst %.cpp, bin/%.o, $(notdir $(SRCS)) )
EXES := $(patsubst %.cpp, bin/%.exe, $(notdir $(wildcard  main/*.cpp)) )

VPATH = main:src

#### rules (target: dependencies -> actions)

.PRECIOUS: $(OBJS)

main: $(EXES)

lib: lib/libFC.a

lib/libFC.a: $(OBJS)
	@echo 
	@echo ---------------------- making lib...[$^] ----------------------
	ar ruv $@ $^
	ranlib $@

bin/%.exe: bin/%.o lib/libFC.a
	@echo 
	@echo ------------------ making executable... $^ [$@] ------------------
	$(CXX) $(CXXFLAGS) -o $@ $< $(INCS) $(LIBS)

bin/%.o: %.cpp
	@echo 
	@echo ---------------------- compiling... $< [$@] ----------------------
	$(CXX) $(CXXFLAGS) -c -o $@ $< -I src $(INCS) $(ROOT_LIB)

clean:
	@echo cleaning...
	rm -f $(wildcard bin/*) $(wildcard lib/*)

dump:
	@echo SRCS...[$(SRCS)] [$(OBJS)] [$(EXES)]
	@echo $(OS)

The Functor.h and Functor.cpp files are in the src/ folder and the main.cpp is in the main/ folder. In the folder above this ones its my makefile and to compile and link I simply do: make bin/main.exe
To run the program: bin/main.exe

I managed to make it work with:

main.cpp :

#include "Functor.h"

int main()
{
   TApplication app("app",nullptr,nullptr);
   Functor F("MyFunctor");

   F.Draw();

   auto *c = F.GetCanvas();
   TRootCanvas *rc = (TRootCanvas *)c->GetCanvasImp();
   rc->Connect("CloseWindow()","TApplication",gApplication,"Terminate()");
   app.Run();
}

Functor.cpp :

#include "Functor.h"

using namespace std;

Functor::Functor(string s) :name(s)
{
   c = new TCanvas("c","c",900,800);
}

void Functor::Draw()
{
   double x[10], y[10];

   // Values of TGraph
   for(int i=0; i<10 ; i++) {
      x[i] = i;
      y[i] = 2*i + 1;
   }

   auto graph = new TGraph(10, x, y);

   // Aesthetics
   graph->SetTitle("MyGraph");
   graph->GetXaxis()->SetTitle("x");
   graph->GetYaxis()->SetTitle("y");
   graph->SetLineColor(kRed);
   graph->SetLineWidth(2);
   graph->SetMarkerColor(kRed);
   graph->SetName("graph");

   graph->Draw("APL");

   // Legenda
   auto legend = new TLegend(0.1002227,0.8195876,0.2639198,0.9007732);
   legend->SetHeader("Legends","C");
   legend->AddEntry("graph","Funtion","l");
   legend->Draw();

   c->Update();
}

TCanvas *Functor::GetCanvas()
{
  return c;
}

Thank you very much! One last thing: so what makes my canvas get the “suff” like TGraph, TF1, etc… its the c->update() line? If c was instead an argument of the Draw method it would also work?

Each time you ‘Draw’ something it is added it the current pad/canvas.