dimanche 31 décembre 2023

How to compile an OpenGL project in CMake in Wayland desktop environment?

I'm trying to setup a project that uses OpenGL as library for graphic development; in particular, I want to use CMake for compiling the project to make it cross-platform. I'm running the project on Arch Linux distro with KDE as desktop environment.

This is my CMakeLists.txt:

cmake_minimum_required(VERSION 3.5.0)
project(run VERSION 0.1.0 LANGUAGES C CXX)

include(CTest)
enable_testing()

add_executable(run main.cpp)
target_link_libraries(
    run PUBLIC
    /usr/lib/libglut.so.3
    /usr/lib/libOpenGL.so.0
    /usr/lib/libglfw.so.3.3
    /usr/lib/libGLEW.so.2.2
    /usr/lib/libdecor/plugins-1/libdecor-gtk.so
)
################################################
set(INCLUDE_ROOT_FOLDER "/usr/include")

target_include_directories(
    run PUBLIC
    ${INCLUDE_ROOT_FOLDER}/GL
    ${INCLUDE_ROOT_FOLDER}/GLFW
    ${INCLUDE_ROOT_FOLDER}/libdecor-0
    ${INCLUDE_ROOT_FOLDER}
)
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)

and this is main.cpp:

#include <iostream>
#include <glew.h>
#include <glut.h>
#include <GLFW/glfw3.h>

int main()
{
    glutInitDisplayMode(GLUT_RGB);
    glfwInit();
    return 0;
}

When compiling, everything is fine. However, this is the error I get at run-time: xkbcommon: ERROR: couldn't find a Compose file for locale "it_IT..UTF-8" (mapped to "it_IT..UTF-8") libdecor-gtk-WARNING: Failed to initialize GTK Failed to load plugin 'libdecor-gtk.so': failed to init

mercredi 27 décembre 2023

Replace boost::timed_wait() with std in c++17

I have a condition variable std::condition_variable my_cond;

I want to be able to replace boost::timed_wait() with an std equivalent one.

If the previously mentioned condition variable was a boost one, then I would use the boost version of timed_wait() like this: cond_.timed_wait(lock,timeout);

where lock is std::scoped_lock lock(mutex_); and timeout is a time that is performed with boost::chrono.

boost:chrono, boost locks and boost mutexes have been replaced with their std analogues. I have yet to find an alternative to timed_wait().

I try to find a function belonging to std, where it receives the lock as the first parameter and an std::chrono type time as a second parameter. Looking at the documentation of the condition_variable::wait here: https://en.cppreference.com/w/cpp/thread/condition_variable/wait

I don't see an overloaded wait function where the second argument is a time type.

Is this conversion from boost::timed_wait() to std::wait() possible?

mardi 19 décembre 2023

Overloaded Templates and non-template functions

I encountered a problem about "Overloaded Templates and non-template functions".

#include <string>
#include <sstream>
#include <iostream>

using namespace std;

// print any type we don't otherwise handle
template <typename T>
string debug_rep(const T &t) {
    ostringstream ret;
    ret << t; // uses T's output operator to print a representation of t
    return ret.str(); // return a copy of the string to which ret is bound
}

// print pointers as their pointer value, followed by the object to which the pointer pointsv
template <typename T>
string debug_rep(T *p) {
    ostringstream ret;
    ret << "pointer: " << p;  // print the pointer's own value
    if (p) {
        ret << " " << debug_rep(*p);  // print the value to which p points
    } else {
        ret << " null pointer";  // or indicate that the p is null
    }
    return ret.str();  // return a copy of the string to which ret is bound
}

string debug_rep(char * p) {
    return debug_rep((string)p);
}

string debug_rep(const char * p) {
    return debug_rep((string)p);
}

int main() {
    string debug_rep(const string& s);
    const char * cp = "hello";
    cout << debug_rep(cp) << endl;

    return 0;
}

string debug_rep(const string& s) {
    return '"' + s + '"';
}

In the book "C++ Primer", they say the cout << debug_rep(cp) << endl; in main function will call debug_rep(const char * p). However, through my test, it actually calls debug_rep(const string& s). So, I wonder why this happened.

samedi 16 décembre 2023

How virtual keyword in virtual inheritance causing a error [duplicate]

I have two set of code :

First case :

#include<iostream>
using namespace std;
class A {
public:
    virtual void display() { cout << "A display \n"; }
};

class B : virtual public A {
public:
    void display() { cout << "B display \n"; }
};

class C : virtual public A {
public:
    void display() { cout << "C display \n"; }
};

class D : public C, public B {
public:
    D() {};
};

int main() {
    D d;
    d.B::display(); // ERROR
}

Error :

Error (active)  E0318   override of virtual function "A::display" is ambiguous
Error   C2250   'D': ambiguous inheritance of 'void A::display(void)'

Second case :

#include<iostream>
using namespace std;
class A {
public:
    void display() { cout << "A display \n"; }
};

class B : virtual public A {
public:
    void display() { cout << "B display \n"; }
};

class C : virtual public A {
public:
    void display() { cout << "C display \n"; }
};

class D : public C, public B {
public:
    D() {};
};

int main() {
    D d;
    d.B::display(); // Output B display
}

The only difference in above two cases is in first one I am making "virtual void display()" in class A, why it is failing in first case while passing in second case?

mercredi 6 décembre 2023

How to Extend C++ Type Traits to Support Pointers and References?

I'm working on a type traits class in C++ and need some assistance in extending its functionality to cover pointers and references. Currently, my class provides traits to check if a type is derived from another, if it's a class or struct, and if it's a primitive or integer type. However, it fails when dealing with pointers or references, as it only considers the direct type.

Here is the existing implementation:

template <typename Derived, typename Base>
using is_derived_from = std::is_base_of<Base, Derived>;

template <typename Type>
using is_class = std::is_class<Type>;

template <typename primitiveT>
using is_primitive = std::integral_constant<bool,
    std::is_floating_point<primitiveT>::value ||
    std::is_integral<primitiveT>::value>;

template <typename TypeInt>
using is_integer = std::integral_constant<bool,
    std::is_integral<TypeInt>::value && !std::is_same<TypeInt, bool>::value>;

template <typename Type, typename Expected>
using is_valid_type = std::integral_constant<bool,
    (is_primitive<Type>::value && std::is_same<Type, Expected>::value) ||
    (is_class<Type>::value && is_derived_from<Type, Expected>::value)
>;

I want to modify is_valid_type to also correctly handle pointers and references. For example, if Type is a pointer or reference to a class derived from Expected, or a pointer/reference to a primitive type that matches Expected, it should still be considered valid.

I've considered using std::remove_pointer and std::remove_reference, but I'm unsure how to integrate them effectively without complicating the code too much.

Any suggestions on how to modify is_valid_type or the approach in general to handle pointers and references efficiently and elegantly in C++?

mardi 5 décembre 2023

std::function - function uses ‘auto’ type specifier without trailing return type in C++ 11 [duplicate]

I'm trying to get a C++14 library working with our C++ 11 code base and having trouble with using std::function (of which I know very little).

Minimal code:

#include <functional>
#include <iostream>

class A
{
public:
    A() {}
    ~A() {}
    
    using CallbackFn = std::function<int(int, double)>;
    auto GetCallbackFn() const { return mCallbackFcn.target<int (*)(int, double)>(); }

private:
   CallbackFn mCallbackFcn;

};

int main()
{
    std::cout << "hi there\n";
}

The code compiles without error with C++14 but with C++11 produces an error:

main.cpp:20:5: error: ‘GetCallbackFn’ function uses ‘auto’ type specifier without trailing return type

I've tried replacing auto with CallbackFn and other permutations but all end up with one error or another.

How do I modify the code to compile under C++11?

Thanks

lundi 4 décembre 2023

How to translate a np.array.view() operation to c++ function

How can I perform this operation in c++ : y = y.view(dtype=int64) * (2**-18), where y is a numpy array where each element holds 16 uint8_t values (128-bits in total).

This is my input y :

[0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0],
[0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0],
[209  97 173 255 255 255 255 255 157 210  83   0   0   0   0   0],
[ 99 139 170 255 255 255 255 255   4 214  86   0   0   0   0   0],
[ 46 172 247 255 255 255 255 255 225 125   8   0   0   0   0   0],
[178 109 184 255 255 255 255 255 223 166  72   0   0   0   0   0],
[0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0],
[0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0]

and here is the expected output:

y_fpga: [[  1.           0.        ]
 [  1.           0.        ]
 [-20.65447617  20.95567703]
 [-21.36388016  21.70899963]
 [ -2.08185577   2.12292862]
 [-17.89287567  18.16296005]
 [  1.           0.        ]
 [  1.           0.        ]]

What are the rules for uniform initialization of a union [duplicate]

If i compile the following code ...

int main() 
{
    union U
    {
        struct { int i, j; } s;
        long long x, y;
        int z;
    };
    U li { 1 };
}

... either x or y becomes initialized with 1 from what I can deduce from the didsassembly. I noticed that when initializing a LARGE_INTERGER with Win32, where the QuadPart becomes initialized, which is very convenient for me.
But what are the rules for static initalization of unions ? Which part of the union is applied with the initialization value if there's no constructor ?
Edit: I've removed the language-lawyer tag someone applied here because I won't judge on that.

C++11 Memory model: the compiler is restricted to don't introduce new data races

Watching a CppCon talk by Michel Wong on 2015 (called C++1/14/17 atomics and memory model... about minute 33:00), he said two sentences that I didn't understand:

No compiler transformation is allowed to introduce a data race (more restrictions on invented writes and possibly fewer speculative stores and potentially loads)

What kind of "invented writes" and "speculative stores and loads" did compiler do, which of those were "common" before the C++11 memory model but now are forbidden? Were those important optimizations that are now lost? Or they had a tiny impact anyway?

There are atomic memory operations that don't cause races (or they race but are well-defined)

What does he mean here by "there are"? In the hardware or does he is refering to some C++ built-in operations? Which memory operations can "cause races but are well-defined"?

dimanche 3 décembre 2023

Generic Container Template Supporting Various Data Structures in C++ 14

Hi StackOverflow community,

I am working on creating a generic container class template in C++ that can support various data structures like std::vector std::map , etc. My goal is to make this container flexible enough to handle different types of data structures and provide a uniform interface for operations like Add and Remove.

Here is the code snippet that I have so far:

With this implementation I can create a general interface for my container, but I'm specifically looking to improve the functionality of std::map. I want to be able to add and remove items for maps in two ways:

`#include <iostream>
#include <vector>
#include <map>

template <typename T>
class IContainer
{
public:
    virtual void Add(const T& item) = 0;     // Adds item
    virtual void Remove(const T& item) = 0;  // Remove the item if it exists in the container
    virtual ~IContainer() = default;
};

template <typename T, typename ContainerType>
class Container : public IContainer<T>
{
public:
    void Add(const T& item) override
    {
        container.Add(item);
    }

    void Remove(const T& item) override
    {
        container.Remove(item);
    }

private:
    ContainerType container;
};

template <typename T>
class VectorContainer
{
public:
    void Add(const T& item)
    {
        vect.push_back(item);
    }

    void Remove(const T& item)
    {
        // Assuming T has a valid equality comparison
        auto it = std::remove(vect.begin(), vect.end(), item);
        vect.erase(it, vect.end());
    }

private:
    std::vector<T> vect;
};

template <typename Key, typename Value>
class MapContainer
{
public:
    void Add(const std::pair<const Key, Value>& keyValue)
    {
        container.insert(keyValue);
    }

    void Add(const Key& key, const Value& value)
    {
        container[key] = value;
    }

    void Remove(const std::pair<const Key, Value>& keyValue)
    {
        container.erase(keyValue.first);
    }

private:
    std::map<Key, Value> container;
};

int main()
{
    Container<int, VectorContainer<int>> myCont;
    myCont.Add(1); // myCont has a generic interface thanks to IContainer

    Container<std::pair<const std::string, int>, MapContainer<std::string, int>> mapCont;
    mapCont.Add({"key1", 42});
    mapCont.Add("key2", 88); // Using the new Add function (not working)

    return 0;
}
`

Using std::pair as a single parameter. Using separate key and value parameters. I implemented the Add method for both cases, but the container does not support separate keys and key values, so I'm not sure if this is the most efficient or correct way to achieve this flexibility. I'm also looking for suggestions to make this template more compatible with other container types.

Can someone provide insight or improvements on how to do the following:

Efficiently support both append methods for std::map. Generalize this approach to effectively cover other container types. Any advice or code sample would be greatly appreciated!

Thank you!

samedi 2 décembre 2023

Lack of LLVM support

LLVM is such a powerful tool, it is very well established and has a large community. As an undergraduate student I had project and needed to use the LLVM API. Words cannot describe how traumatised I am. I understand that LLVM is huge and very well documented, but I found it extremely hard to do even the most basic tasks. I just wanted to see if others face this problem or is it just me? Are people using LLVM already senior developers with years of experience or do companies such as Apple, Google, etc. gatekeep their resources? I am just very interested in the aspect of low-level compiler design in the future, so any advice would be very much appreciated!

Not only did I spent hours on the official website trying to find my way though, which was like navigating an online labyrinth from the 1990s, I also spent a large amount of time trying to find other web resources/forums, that were mostly useless!

Filter sober in c++ don't pass test

I am working on implementing the Sobel filter for image processing in C++. However, I am encountering unexpected results and failing test cases. I have reviewed the code multiple times, but I cannot pinpoint the source of the issue.

I have provided the relevant parts of my code below. The intensiteH and intensiteV functions are intended to calculate the horizontal and vertical gradients using the Sobel operator, and the intensite function combines these gradients to compute the overall intensity.

#include <stdexcept>
/** @file
 * Filtres de Sobel
 **/
#include <cmath>
#include <vector>
#include <stdexcept>
#include <iostream>
#include <fstream>
using namespace std;

/** Structure de donnee pour representer une image en teintes de gris **/
typedef vector<vector<double> > ImageGris;



/** Infrastructure minimale de test **/
#define CHECK(test) if (!(test)) cerr << "Test failed in file " << __FILE__ << " line " << __LINE__ << ": " #test << endl


/** Une image 4x4 en teintes de gris pour faire des tests **/
ImageGris imgGrisTest = {
    {0, 255, 54.213, 236.589},
    {18.411, 182.376, 200.787, 120},
    {139.583, 172.841, 94.0878, 88.4974},
    {158.278, 172.841, 89.0236, 80.0384}
};



/** Construire une image en teintes de gris depuis un fichier PGM
 * @param source le nom d'un fichier PGM
 * @return une image en teintes de gris
 **/
ImageGris lirePGM(string source) {
   // Déclarations
  ifstream pgm ;    
  pgm.open(source);
  ImageGris imagegris;
       if (!pgm) {
    throw runtime_error("Fichier non trouve: "+source);}
   
 
    if (pgm.is_open()) {
        char type[3]; // stocke type du fichier pbm sous forme de tableau contenant P,2,'\0
        int largeur, hauteur , max ;
       
       // Lecture de l'en-tête du fichier PBM
        pgm >> type >> largeur >> hauteur >> max ;

        if (type[0] == 'P' && type[1] == '2') {
           
          imagegris = vector<vector<double>>(hauteur, vector<double>(largeur)); // car ImageGris est un type                                                                                    et non une variable
           
                                             
                                               
      for (int i=0 ; i<hauteur ; i++){
        for (int j=0 ; j<largeur ; j++){
        pgm>>imagegris[i][j];}        
                                       }
                                     }
   
        pgm.close() ;
                     }
   
      return imagegris ;
                        
}



/** Ecrit une image en teintes de gris dans un fichier PGM
 * @param img une image en teintes de gris
 * @param cible le nom d'un fichier PGM
 **/
void ecrirePGM(ImageGris img, string cible) {
   ofstream pgm ;
  pgm.open(cible);
    if (!pgm) {
    throw runtime_error("Fichier non trouve: "+cible);}
   
    //écriture de l'en-tête
 
     if (pgm.is_open()) {
       pgm<<"P2"<<endl ;  
        pgm<<img[0].size()<<" "<<img.size()<<endl;  
        pgm<<255<<endl;
     
     //" " permet de séparer chaque pixel par un espace  // endl pour aller à la ligne une fois que tous les        pixels sur la ligne ont été écrits
                                                 
      for (int i=0 ; i<img[0].size() ; i++)  {
        for (int j=0 ; j<img.size() ; j++) {
         pgm<<(int)img[i][j]<<" ";         }        
                                             }
        pgm<<endl ;
                         }
        pgm.close();
}








/** Teste si deux images en teintes de gris sont égales modulo imprécision numérique
 * En cas de différence un message est affiché
 * @param a une image en teintes de gris
 * @param b une image en teintes de gris
 * @param precision un flottant positif: la précision souhaitée; typiquement 0.001
 * @return vrai si les images sont égales et faux sinon
 **/
bool ImageGrisEgal(ImageGris a, ImageGris b, float precision) {
    if (a.size() != b.size())  {
        cout << "Nombre de lignes différent" << endl;
        return false;
    }
    for (int i=0; i<a[0].size(); i++)
        if (a[0].size() != b[i].size()) {
            cout << "Nombre de colonnes différent" << endl;
            return false;
        }
    for (int i=0; i<a.size(); i++)
        for (int j=0; j<a[0].size(); j++)
            if (abs(a[i][j] - b[i][j]) > precision) {
                cout << "Valeur differentes en position " << i << "," << j
             << ": " << a[i][j] << " au lieu de " << b[i][j] << endl;
                return false;
            }
    return true;
}

/// BEGIN intensiteH

/** filtre de Sobel horizontal
 * @param img une image en teintes de gris
 * @return une image en teintes de gris de l'intensite horizontale de img
 **/
ImageGris intensiteH(ImageGris img) {
    double gradx ; // gradient horizontal de l'image
    ImageGris intensiteho(img.size(), vector<double>(img[0].size())); //image de retour est de même taille que 
  for ( int i=0; i<img.size();i++){
        for ( int j =0 ; j<img[0].size();j++){
            if (i > 0 && i < img.size() - 1 && j > 0 && j < img[0].size() - 1) {
    gradx = (img[i - 1][j - 1] + 2 * img[i][j - 1] + img[i + 1][j - 1]) - (img[i - 1][j + 1] + 2 * img[i][j + 1] + img[i + 1][j + 1]);
}

        }
   }
 return intensiteho ;
}


/// BEGIN intensiteV

/** filtre de Sobel vertical
 * @param img une image en teintes de gris
 * @return une image en teintes de gris de l'intensite verticale de img
 **/
ImageGris intensiteV(ImageGris img) {
    double grady ; // gradient vertical de l'image
    ImageGris intensitever(img.size(), vector<double>(img[0].size())); //image de retour est de même taille que 
  for ( int i=0; i<img.size();i++){
        for ( int j =0 ; j<img[0].size();j++){
              if (i > 0 && i < img.size() - 1 && j > 0 && j < img[0].size() - 1) {
           grady = img[i-1][j-1] + 2*img[i-1][j] + img[i-1][j+1] - img[i+1][j-1] - 2*img[i+1][j] - img[i+1][j+1] ;
            intensitever[i][j]=grady ;

        }
   }
  }
 return intensitever ;
}


/// BEGIN intensiteHV

/** filtre de Sobel
 * @param img une image en teintes de gris
 * @return une image en teintes de gris de l'intensite de img
 **/
ImageGris intensite(ImageGris img) {
    ImageGris IntensiteHo=intensiteH(img);
    ImageGris IntensiteVe=intensiteV(img);
    ImageGris Intensiteimg(img.size(), vector<double>(img[0].size()));
     for ( int i=0; i<img.size();i++){
        for ( int j =0 ; j<img[0].size();j++){
           double valintensite = sqrt(IntensiteHo[i][j]*IntensiteHo[i][j] +IntensiteVe[i][j]*IntensiteVe[i][j] ) ;
           Intensiteimg[i][j]=valintensite;

        }
    }
    return Intensiteimg ;
}


void testSobel() {
    CHECK( ImageGrisEgal(intensiteH(imgGrisTest),
              ImageGris( {
                  {0, 0, 0, 0},
                  {0, -373.47, 227.507, 0},
                  {0, -22.1312, 323.866, 0},
                  {0, 0, 0, 0}
              }),
              0.001) ); // precision changée
    CHECK( ImageGrisEgal(intensiteV(imgGrisTest),
              ImageGris( {
                  {0, 0, 0, 0},
                  {0, -15.1398, 150.501, 0},
                  {0, -9.0336, 273.023, 0},
                  {0, 0, 0, 0}
              }),
              0.001) );
    CHECK( ImageGrisEgal(intensite(imgGrisTest),
              ImageGris( {
                  {0, 0, 0, 0},
                  {0, 373.777, 272.782, 0},
                  {0, 23.9039, 423.593, 0},
                  {0, 0, 0, 0}
              }),
              0.001) );

    cout << "Vérifier que les images obtenues dans 'sobel/' sont semblables à celles fournies dans 'sobel/correction/'" << endl;
    ecrirePGM(intensite(lirePGM("images/Willis.512.pgm" )), "sobel/Willis.512.pgm");
    ecrirePGM(intensite(lirePGM("images/Baboon.512.pgm" )), "sobel/Baboon.512.pgm");
    ecrirePGM(intensite(lirePGM("images/Billes.256.pgm" )), "sobel/Billes.256.pgm");
    ecrirePGM(intensite(lirePGM("images/Embryos.512.pgm")), "sobel/Embryos.512.pgm");
    ecrirePGM(intensite(lirePGM("images/House.256.pgm"  )), "sobel/House.256.pgm");
}



int main(){

    testSobel();
    return 0 ;
}


I tried to run my code but the tests were failing , here is an example :Valeur differentes en position 1,1: -96.2992 au lieu de -373.47 Test failed in file sobel-tout-en-un.cpp line 200: ImageGrisEgal(intensiteH(imgGrisTest), ImageGris( { {0, 0, 0, 0}, {0, -373.47, 227.507, 0}, {0, -22.1312, 323.866, 0}, {0, 0, 0, 0} }), 0.01) Valeur differentes en position 1,1: 97.482 au lieu de 373.777 Test failed in file sobel-tout-en-un.cpp line 216: ImageGrisEgal(intensite(imgGrisTest), ImageGris( { {0, 0, 0, 0}, {0, 373.777, 272.782, 0}, {0, 23.9039, 423.593, 0}, {0, 0, 0, 0} }), 0.001)

cant understand the logic of the problem specially the last line of the problem need a program written

The following lists the sound level in decibels for several common noises: Noise Decibel Level (dB) Jackhammer 130 Gas lawnmower 106 Alarm clock 70 Quiet room 40 Write a c++ program that reads a sound level in decibels from the user. If the user enters a decibel level that matches one of the noises in the table, then your program should display a message containing only that noise. If the user enters a number of decibels between the noises listed, then your program should display a message indicating which noises the level is between. Ensure that your program also generates reasonable output for a value smaller than the quietest noise in the table and for a value larger than the loudest noise in the table.

cant understand the problem

vendredi 1 décembre 2023

Class with vector of another class doesn't like to change its values

I have a class called intClass that contains a private vector of ints and functions to modify its contents. I have another class called intClassClass that contains a private vector of intClasses and functions to modify and print those. When I attempt to change the innermost ints from the intClassClass, the function runs but the ints don't actually change. I'm pretty sure the solution has something to do with a vector making a copy of the object instead of the object itself, but I'm not sure where that happens or how to fix it.

class intClass
{
private:
    vector<int> ints;
public:
    intClass()
    {
        ints.clear();
    }
    intClass(vector<int> iVec)
    {
        ints = iVec;
    }
    void modifyVec(int newInt, int index)
    {
        cout << "modified vec" << endl;

        ints[index] = newInt;
    }
    void printVec()
    {
        for (int i : ints)
        {
            cout << i << ' ';
        }
        cout << '\n';
    }
    int getInt(int index)
    {
        return ints[index];
    }
};
class intClassClass
{
private:
    vector<intClass> ics;
public:
    intClassClass(vector<intClass> icVec)
    {
        ics = icVec;
    }
    intClass getIC(int index)
    {
        return ics[index];
    }
    void printVec()
    {
        for (intClass i : ics)
        {
            i.printVec();
        }
        cout << '\n';
    }
};

int main()
{
    intClassClass icc = intClassClass({ intClass({ 1,3,4,5,21 }), intClass({2, 4 }) });
    icc.printVec();
    icc.getIC(1).modifyVec(3, 0);
    icc.printVec();
    cout << icc.getIC(1).getInt(0);

    return 0;
}

this outputs this